Click to See Complete Forum and Search --> : a basic c++ question


younger2
November 7th, 2002, 03:39 AM
class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;

};

who can tell me the meaning of this line?
//rational(int numerator = 0, int denominator = 1);

I know it's construct function. I want to know the meaning of " int numerator=0".

Thanks

afzal
November 7th, 2002, 04:06 AM
its a constructor with two arguments numerator and denomenator intialized with some values

younger2
November 7th, 2002, 04:56 AM
Thank you for reply. But why link error?

class rational {
public:
rational(int numerator = 0, int denominator = 1);
int numerator() const;
int denominator() const;

};

int rational::numerator() const {
........
}


int main()
{ rational r1;
cout << r1.numerator() << endl;
}

The error is
ld:
Unresolved:
rational::rational(int, int)
*** Exit 1


Thanks again.

usman999_1
November 7th, 2002, 05:20 AM
Why ru not providing the definition for your contructor.
When u have supplied default arguments then
doing this

rational r1;

or

rational r2(1,2);


EDIT:
OR

rational r3(1);


Gets resolved to the Same constructor


rational(int a = 1, int b = 2);


Hope this Helps,
Regards,
Usman.

dumah
November 7th, 2002, 05:27 AM
Few ideas


#include <iostream>
#include <exception>


class rational {
int m_numerator;
int m_denominator;
public:
rational(int _n = 0, int _d = 1) throw (std::exception)
:m_numerator(_n),m_denominator(_d){
//.....do whatever maybe;
if(m_denominator == 0)throw std::exception();
}
const int numerator(){return m_numerator;}
const int denominator(){return m_denominator;}
};


int main(){
try{
rational r1;
std::cout << r1.numerator() << std::endl;
std::cout << r1.denominator() << std::endl;

rational r2(10,50);
std::cout << r2.numerator() << std::endl;
std::cout << r2.denominator() << std::endl;

rational r3(50,0);//will throw!
std::cout << r3.numerator() << std::endl;
std::cout << r3.denominator() << std::endl;
}
catch(std::exception& e){
std::cout << "Exception caught!" << std::endl;
}
}

younger2
November 7th, 2002, 05:39 AM
Thank you for dumah's professioal example. But I have one question for your sample code:

What's the meaning of "throw " statement right after the constructor ?

rational(int _n = 0, int _d = 1) throw (std::exception)
:m_numerator(_n),m_denominator(_d){
//.....do whatever maybe;
if(m_denominator == 0)throw std::exception();
}

dumah
November 7th, 2002, 06:00 AM
Sorry...yeah...its ans exception specifier - a kind of promise that the function wont throw any exceptions apart from those listed.

throw() after a function means no exceptions will be thrown......not showing an exception specifier means the func can throw anything....

I'm trying to get my exception handling better in my own code, so I'm trying to get in the habit...though I guess I was wrong to put it in a simple example as it would probably make it more confusing :)

younger2
November 7th, 2002, 06:52 AM
Thank you, dumah, I tried your sample and still a little confused about your word"throw() after a function means no exceptions will be thrown......not showing an exception specifier means the func can throw anything....". Thus, in previous code there are throw after the constructor, but there is exception.

If I modified the constructor like this:

rational(int _n = 0, int _d = 1) :m_numerator(_n),m_denominator(_d){
if(m_denominator == 0)throw std::exception();
}

You can see this time no "throw()" func after the constructor. Will main() program catch some exception?

I tried and it can also catch exception. So I want to know if throw() after the constructor or not can make difference.

Thanks for your patience and time.

dumah
November 7th, 2002, 07:28 AM
Originally posted by younger2
Thank you, dumah, I tried your sample and still a little confused about your word"throw() after a function means no exceptions will be thrown......not showing an exception specifier means the func can throw anything....". Thus, in previous code there are throw after the constructor, but there is exception.

If I modified the constructor like this:

rational(int _n = 0, int _d = 1) :m_numerator(_n),m_denominator(_d){
if(m_denominator == 0)throw std::exception();
}

You can see this time no "throw()" func after the constructor. Will main() program catch some exception?

I tried and it can also catch exception. So I want to know if throw() after the constructor or not can make difference.

Thanks for your patience and time.

You can certainly leave it out of the declaration...it just means that the constructor can throw any exception......in my code, the only thing that can be thrown is std::exception or anything derived from it.....In practice here, its not an issue, the exception will be caught....but if this code became a part of a big library, then its a nice guarantee for a user that the constructor can only throw 1 type of exception, so the user only provides for this 1 case.

Interestingly, if you specify what exceptions are thrown and then break that promise, the exception is not thrown, but the app terminates. Look at the following, you would expect the exception to be caught, but it wont


#include <iostream>
#include <exception>

class foo{};//Some class

void bar()throw(){//promise not to throw

throw foo();//break promise
}

int main(){

try{
bar();
}
catch(...){//falsely expect all exceptions from bar to be caught

std::cout << "Exception caught" << std::endl;
return 1;
}

std::cout << "No Exception caught" << std::endl;
}


What actually happens is a special handler is called, and that normally terminates.....you can specify a custom handler though like this


#include <iostream>
#include <exception>
#include <cstdlib>

class foo{};//Some class

void bar()throw(){//promise not to throw

throw foo();//break promise
}

void My_Handler(){//handler to be called

std::cout << "Handler invoked!!!" << std::endl;
std::abort();
}

int main(){

std::set_unexpected(My_Handler);//set handler

try{
bar();
}
catch(...){//falsely expect all exceptions from bar to be caught

std::cout << "Exception caught" << std::endl;
return 1;
}

std::cout << "No Exception caught" << std::endl;
}


Also interesting, you can rethrow in the handler and then the exception (which could theoretically be anything is changed into a std::bad_exception......so if you set the handler to rethrow, and then specify bad_exception, you will catch the exception no matter what was actually thrown.....this might be handy for misbehaving code that you have no access to...


#include <iostream>
#include <exception>

class foo{};//Some class

void bar()throw(std::bad_exception){//throw bad_exception

throw foo();//break promise
}

void My_Handler(){//handler to be called

std::cout << "Handler invoked!!!" << std::endl;
throw;
}

int main(){

std::set_unexpected(My_Handler);//set handler

try{
bar();
}
catch(std::exception){//note bad_exception is derived from exception

std::cout << "Exception caught" << std::endl;
return 1;
}

std::cout << "No Exception caught" << std::endl;
}


Exceptions - tricky devils ;)

Andreas Masur
November 7th, 2002, 08:42 AM
Originally posted by younger2
Thank you, dumah, I tried your sample and still a little confused about your word"throw() after a function means no exceptions will be thrown......not showing an exception specifier means the func can throw anything....".
The form of this function declaration is called exception specification. The essential benefit of an exception specification is that clients of a component know exactly what exceptions may be thrown from a function or a method. This can be great from the client perspective of handling exceptions, because the client knows exactly what exceptions, if any, may be thrown.

Consider the following code:

class CFoo
{
public:
void FunctionWithOneExceptionSpecification() throw (ExceptionClass);
void FunctionThatThrowsNoExceptions() throw();
};
The first exception specification says that exceptions of type 'ExceptionClass' or derived from 'ExceptionClass' may be thrown from that method.

However, to implement this specification you will most likely have to use a 'try'/'catch' block within the implementation of these methods to enforce the exception specification. Otherwise you run the risk of 'std::unexpected()' being called, the default behavior of which is to terminate the application. And since aborting the program execution is usually much more catastrophic than letting an exception propagate all the way up you have to fall back on using the 'try' block.

The second function is declared as not throwing any kind of exception. However, if you did not write the function itself you will never know if this function still can throw an exception. Then you need to have a serious discussion with the developer of the function about exception handling...

Graham
November 7th, 2002, 11:40 AM
... but the big problem with putting exception specifications in your code is that if you miss something then you run the risk of getting the dreaded "unexpected" exception. This will almost certainly result in an immediate crash. This is not a rare problem either: remember that your exception specification must include exceptions that can be thrown from anything that you call within the function. For example,

void f() throw(my_exception)
{
//......
foo *f = new foo(); // new can throw std::bad_alloc
//......
if (something_bad_happens)
throw my_exception;
}

This is a pretty trivial example, admittedly, and easily spotted, but it illustrates the point. If the new throws its exception, then f() is breaking its contract to only throw "my_exception", and the program will crash.

About the only useful throw specification is "throw()" to explicitly state that this function will not throw under any circumstances. functions that swap two objects are often declared (and implemented!) as non-throwing functions, so that exception-safe copying can happen. For example:

class foo
{
int m1;
int m2;

public:
//...
foo(const foo&);

foo& operator=(const foo& rhs)
{
foo temp(rhs);
swap(temp);
return *this;
}

void swap(foo& rhs) throw()
{
std::swap(m1, rhs.m1);
std::swap(m2, rhs.m2);
}
};

The function foo::swap is guaranteed not to throw an exception. foo::operator=() is exception safe since it makes a local copy of the rhs (which might throw) before committing that change. In other words, if the foo copy ctor throws, then the object being assigned to comes out of it unchanged (so you've got some hope of recovering). Only if the copy constructor succeeds does the object being assigned get modified, and that with a non-throwing swap.

See Herb Sutter's Exceptional C++ for more detail.