tags:

views:

234

answers:

5

Have a class with couple of integers and a pointer ,

 class A {
     int a;
     int b;
     char* s;
    public:
    ...
      class ConstructA {
           A &a;
           public:
           ConstructA (A& ta) : a(ta) {}
           ...

      };

};

As seen ConstructA is responsible for constructing object A. I want to write a method to see if ConstructA was successful in constructing the object a. How would you go about it ?

+7  A: 

Since there is no way for a constructor to fail in C++, an object either:

  • does not exist
  • is successfully constructed.

If a constructor throws an exception, the object effectively does not exist at the point the exception is caught.

You may want to implement an additional method such as isOK() that returns whether or not the internal state of the object represents something useful. For example, if you have an object that represents a file, then you could use isOK() to indicate that the file could be successfully opened. However, as far as C++ is concerned, the file object would be fully constructed whether or not the file could be opened.

Having said that, I'm not entirely sure what the role of the ConstructA class is in your example.

Greg Hewgill
A: 

As a general rule, if there is any failure in construction you will get an exception thrown -- either a program-specific exception from the object itself, or else a memory exception or something similar from the runtime.

In this particular case, you are not constructing an object of type A, you are initializing a reference. So there is no construction that has an opportunity to fail.

(Perhaps a little more detail about what you are trying to accomplish would help somebody give a better answer?)

Eric
A: 

First, your ConstructA class makes no sense at all to me. There really are two cases basically:

  1. The construction of an object fails
  2. The object is left in an defined, but restricted state. Strictly, the construction succeeded, but possibly not the the degree that the caller wished. He can optionally later complete the construction.

1. The construction fails

I'll use your ConstructA in the following code just to make the point of signaling about construction failure:

A a0;
try {
    A::ConstructA a1(a0);
} catch(...) {
    // fail to construct a1
}

Constructors throw exceptions to signal when their constructions fail. The idea is that if construction of an object fails, then the object is not constructed. In other words, the object does not exist. If you have to initialize a1 in an constructor init list, there is a way too:

struct B {
    B() try : a1(a0) { 
        // other constructor code...
    } catch(...) {
        // fail to construct either a0 or a1, or constructor 
        // code threw
    }
    A a0;
    A::ConstructA a1;
};

try {
    B b;
} catch(...) {
    // fail to construct B
}

First, code in the catch block of the constructor is executed, then the exception is automatically propagated to the code that created the B object, and its catch blocks are considered.

Your class ConstructA's constructor could look like this, for example:

struct ConstructA {
    ConstructA(A &a):a(a) {
        if(!condition_met) {
            throw std::runtime_error("condition_met false");
        }
    }
    A &a;
};

2. The object is in a restricted state

That is exactly the case what you have when you have a file stream object, but which is not connected to a file. If in the constructor you find that the file mentioned doesn't exist, you can still fully construct the stream, but set it in a not-opened state. Some operations could be disallowed then and you can have a isOpen or isValid function that you can use to query the state of the object. This can be applied to other cases too. Some framework i used has a Color class. Default constructing a color class left it in a invalid state, so that it doesn't compare equal to any valid color:

Color c;
assert(c.isValid() == false);

The same technique is used for a null pointer. It's set to a defined, but restricted state when you assign NULL to it:

int *a = NULL;
assert(a == NULL);

You cannot dereference a null pointer for example.

Johannes Schaub - litb
In block 1, snippet 1: the construction takes place in the first line, not in ConstructA constructor (which only stores a reference and will not fail)In block 1, snippet 2: the contruction takes place in an implicit call to a0() in the initializer list.
David Rodríguez - dribeas
dribeas, as i said i don't understand the purpose of his class. he wanted to watch the construction of ConstructA, not the one of A. so i only watched that ones :)
Johannes Schaub - litb
I agree he should make it not a reference, as Josh points out. but then that wouldn't change everything. what could throw then is the copy constructor of A. and that one would throw from within ConstructA, of course.
Johannes Schaub - litb
A: 

You're ConstructA is just trying to hold on to a reference of an already constructed A, try changing it to this:

  class ConstructA {
       A a; // Don't make this a reference
       public:
       ConstructA (A& ta) : a(ta) {}
       ...

  };
Eclipse
A: 

mkal, I always suggest people not to use exception in C++. It cause more problems that it tries to solve.

To answer your question, to avoid exception in C++, you probably can reduce the work you want to do in your constructor, and create an init() method instead, that returns the error-code if something goes wrong.

Basically, whatever you do in C++ Constructor, it has to succeed.

Hope that helps.

KOkon
Using an init() method is much more error-prone. Its easy to forget to call init(), especially if you are dynamically creating lots of objects. Used correctly, exceptions are a much better option.
KeithB
Maybe you should check the Google style-guide. It gives a better explanation than I do:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions
KOkon