why exceptions?

R

ramu

Hi,
Why exceptions ?
Suppose we are writing a code like this

class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}

Like that we can check any issue using if conditions. Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!
 
V

Victor Bazarov

ramu said:
Hi,
Why exceptions ?
Suppose we are writing a code like this

class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}

Like that we can check any issue using if conditions.

No, you can't.
> Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!

First off, 'new' does not return NULL unless you use 'std::nothrow'.
'new' *throws*. And you better catch it.

Second, just like with the Standard library, there are libraries out
there which have code that throws exceptions instead of returning error
(or success) codes. It's simply considered *easier* to throw in case of
outstanding circumstances than to pull the return value through multiple
layers of calls.

Third, when you perform calculations in an expression, the value
returned from each operator function needs to be the object that can be
used further in the same expression. Where do you stick the error code?

So, find a good book on C++ and read about the exceptions. What book
have you already read? What in that book is unclear WRT exceptions?
Does the author actually say why the exceptions are needed? I would
hazard a guess that you either haven't read any book, or you have
skipped the exceptions chapter, or the language was too difficult to
comprehend and you decided to see if somebody can simplify it for you...

V
 
S

SG

class test {  };
int main()
{
  test *ob = new test;
  if (ob == NULL)
    cout<<"Memory is not allocated"<<endl;

This test is useless. Either allocation and initialization was
successful or an exception is thrown.
  //....
  //...
}

Like that we can check any issue using if conditions. Then what is
the need to put the code inside a try block and have a catch block
for that. This may be only a overhead ?!

The idea of exceptions is to separate "error detection" from "error
handling". In your example you do both in one function: if (condition)
handle_error(); But sometimes you don't know how to handle error cases
and in those cases it can be convenient to just throw an exception.
The exception handler doesn't have to be in the same function body.

Cheers!
SG
 
L

Lars Tetzlaff

ramu said:
Hi,
Why exceptions ?
Suppose we are writing a code like this

class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}

Like that we can check any issue using if conditions. Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!

See Bjarne Stroustrups FAQ at
http://www.research.att.com/~bs/bs_faq2.html#exceptions-why

Lars
 
J

Juha Nieminen

ramu said:
Like that we can check any issue using if conditions. Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!

Suppose you do something like this:

std::vector<MyClass> v(something.begin(), something.end());

How would you best handle all the possible error situations that might
arise from that single line? Suppose that the vector runs out of memory
while trying to construct itself. Or suppose that the objects being
copied encounter an error during the operation. How would you catch
these error situations?
 
J

James Kanze

Why exceptions ?
Suppose we are writing a code like this
class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}
Like that we can check any issue using if conditions. Then
what is the need to put the code inside a try block and have a
catch block for that. This may be only a overhead ?!

For this particular program, there would really be no problem if
the new operator returned a null pointer, and you were required
to check it. Most applications are a lot bigger, with a lot of
operator new, in a lot of different places. Having to check
everywhere would be a real pain, especially since you generally
can't handle the error immediately---you'd have to propagate it
up. Which means that any code which calls a function which
might allocate memory has to check its status.

And of course, a lot of new expressions are in constructors,
where you don't have a return code. So you'd have to introduce
an additional zombie state in the object, and test it just about
everywhere.

For errors which can reasonably expect to handle in the
immediate calling code, return codes are fine. For errors which
can really only be handled at a higher level, or for errors
which prevent establishing important invariants, exceptions are
a far better mechanism.
 
P

Pascal J. Bourguignon

ramu said:
Hi,
Why exceptions ?
Suppose we are writing a code like this

class test { };
int main()
{
test *ob = new test;
if (ob == NULL)
cout<<"Memory is not allocated"<<endl;
//....
//...
}

Like that we can check any issue using if conditions. Then what is the
need to put the code inside a try block and have a catch block for
that. This may be only a overhead ?!


For a single function call, of course it's not obvious. But programs
are more complex, and it's a good thing to be able to combine function
calls in a single expression.

For example:

void doUpdateSelectedShopInventory(UserContext userContext){
try{
doTheSameChangeInShop(getSelectedShop(userContext),
getChangedDataFromInventory(getInventory(userContext),
getLastSynchronizationDate(userContext)));
}catch(NeverSynchronized& e){
copyInventoryToShop(getInventory(userContext),getSelectedShop(userContext));
}catch(InventoryNotChanged& e){
reportWarning("That inventory didn't change");
}
}


Notice that we didn't handle the DatabaseException, and the
NoShopSelected exceptions. These will be caught and handled by the
functions that call doUpdateSelectedShopInventory(). Here, we just
handle what we know how to handle.

try{
switch(userCommand){
case CmdUpdateSelectedShop:
try{
doUpdateSelectedShopInventory(userContext);
}catch(NoShopSelected& e){
reportMessage("Please select a shop first");
}
break;
...
}
}catch(DatabaseException& e){
reportError("Cannot use the database now, please try again later.");
}


The main problem if you try to avoid exceptions, is that you cannot
combine function calls any more. Either the returned value encodes an
error, or you have an additionnal output parameter to signal an error.
In both cases, unless you implement what I describe in:

http://groups.google.com/group/comp.programming/msg/2b899c6850cc731d?hl=en (*)

you would have to transform the function code into procedural code such as:


ObjectWithError doUpdateSelectedShopInventory(UserContext userContext){
Shop shop=getSelectedShop(userContext);
if(shop->hasError()){
if(shop->isErrorNoShop()){
reportMespsage("Please select a shop first");
// Well, what if this function is not called in the
// context of a user selected shop, but that of a batch
// processing?
}else{
// what should we do?
return(something);
}
}else{
Inventory inventory=getInventory(userContext);
if(inventory->hasError()){
return(something); // notice, Shop is not Inventory, we need to return a superclass?
}
Date date=getLastSynchronizationDate(userContext);
if((date->hasError())
and (date->errorNeverSynchronized())){
copyInventoryToShop(inventory,shop);
}else if(date->hasError()){
return(something);
}else{
InventoryChanges data=getChangedDataFromInventory(inventorydate);
if(data->hasError()){
if(data->errorInventoryNotChanged()){
reportWarning("That inventory didn't change");
}else{
return(something);
}
}else{
doTheSameChangeInShop(shop,data);
}
}
}
}

So you can see that the result is rather unreadable, less maintainable
(be careful with these if/else...), imposes more complexities in the
object model unrelated to the domain (include "error" in Shop,
Inventory?, put them in an unrelated superclass such as
ObjectWithError?), makes you handle errors in the wrong place, or to
handle the transmission of the error up to the callers. In effect,
you are badly and inefficiently re-implementing exceptions. (See
Greenspun's Tenth Law).




(*) In C++, you could use templates instead of macros to wrap the
types like in this example, but you will admit that it's a lot of
work...
 
J

Juha Nieminen

Paavo said:
No offense, but the language would be Turing complete also without
exceptions

So what? My washing machine is Turing complete. That doesn't really
say much.
so one could certainly do the error checking otherwise as
well (requires redesign of whole STL, of course). E.g.:

std::vector<MyClass> v(something.begin(), something.end());
if (!something.is_good() || !v.good()) {
this->is_good = false;
return;
}

It would be a bit cumbersome for 'something' to know that some of its
elements (which it knows next to nothing about) encountered an error
while being copied to another container.
int errorcode=0;
std::vector<MyClass> v(something.begin(&errorcode), something.end
(&errorcode), &errorcode);
if (errorcode!=0) {
return errorcode;
}

It would be equally cumbersome for the iterators in 'something' to
know this about the objects which they are pointing to (which they still
know next to nothing about).
if (something.empty()) {
std::cerr << "precondition broken\n";
exit(1);
}

Why would 'something' empty itself if copying some of the elements failed?
// std::vector ctor will exit(1) by itself if something wrong
std::vector<MyClass> v(something.begin(), something.end());

Hard to do something specific to this error if it just calls exit(1).
 
M

mzdude

Hi,
      Why exceptions ?
Suppose we are writing a code like this

class test {  };
int main()
{
  test *ob = new test;
  if (ob == NULL)
    cout<<"Memory is not allocated"<<endl;
  //....
As others have pointed out, this
test *ob = new test;
is really a two step process. Just testing for NULL doesn't tell
you if the memory allocation failed or the constructor for test
failed.
try {
test *obj = new test;
}
catch( std::bad_alloc const & ba )
{
.... // do something in low memory condition
}
catch( std::runtime_error const &re )
{
... // do something because ctor failed
}

This may be an improvement, but I'll wager 80% or more (insert your
favorite number here) of programmers have no idea of how to handle
a low memory error. I'll include myself in that 80%. What happens
if the object is relatively small? Say 10's or 100's of bytes.
Chances are any subsequent recovery functions will require memory
and will fail also. If the object was
very large, you might stand a chance of reporting the error
and doing a graceful shutdown, if that is even an option. All
the programs I write are for the PC desktop and no human
life is at stake. I don't think I've had to handle an out of memory
error since probably 2000. I do have to handle errors when
resources aren't available though. As shown above, my job is made
easier with the use of exceptions.

Typically I surround a major functional block with the try/catch
and don't try to handle recovery on a low level basis. For example

void HandleUserMenuSelectionToDoSomething()
{
try {
DoSomethingVeryImportant();
}
catch( FileTypeErrors const &fe ) {}
catch( ResourceTypeErrors const &re ) {}
catch( ... ) {}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,158
Messages
2,570,882
Members
47,414
Latest member
djangoframe

Latest Threads

Top