You Should Avoid Partial Initialization of Objects

 

Recently a code came to me for a review. I’m not exposing the real code but just scribbling a sample code like as it is.

#include <iostream>

using namespace std;
#define LOG_ERROR(msg) cerr<<msg<<endl
// Just a manager class. the members and other details are irrelevant here
class CManager
{ };
class CRender
{
private:
// Status whether the object is initialize or not
bool m_bInitialized;
CManager* m_pManager; // Manager Pointer
public:
// Ctor
CRender( CManager* pManager )
{
    if( !pManager )
    {
        LOG_ERROR(“Invalid pointer to Manager”);
        return; // You shouldn’t return at this point. This is dangerous
    }
    m_pManager = pManager;
    m_bInitialized = false;
    }
    // Initialize function
    bool Initialize()
    {
        if( !m_bInitialized )
        {
            cout<<”Do processing”
            m_bInitialized = true;
        }
        else
           LOG_ERROR(“Object already initialized”);
       return m_bInitialized;
    }
};

int main( int argc, char* argv[] )
{
    CRender rend(NULL);
    rend.Initialize();
    return 0;
}

Effective C++ also explains this scenario in prolific manner. An object should be constructed and destructed fully. Partial Initialization will make some unexpected behavior. The Initialize function can be called any number times from outside world. If we pass a NULL to the pointer, the object creation will not be complete since some of the significant members are omitted from its initialization.

Of course there are many ways defend write solid code. But still many people are not thinking in that way. You can also check for the m_pManager pointer many times in the functions where we are using it. In some trusted applications or classes, we will not validates the input because we are quite sure about the parameter passing.

In this case the the m_bInitializedis the catch which is initialized with some unknown values (it may vary depends on the compilers and configuration) we we can’t predict. Now in this case the Object will be in truble with “Default” constructor of that member. Note that this is the case of intrinsic data types. For the objects of some classes, the default Initialization will be done using default constructor or specified constructor in the Initialization list. Your code defines the behavior of your program.

Also I need to add one more point; if we are assigning something inside the ctor, that will be operator= operation than (copy) contruction.

Construction happens in the initialization list. For those members which is not specified in the initialization list, the compiler will initialize them with default constructor or value(compiler specific. for Visual C++, in the debug mode, the default value for initialization is 0xCD per byte).

CRender( CManager* pManager ) : m_pManager(pManager)

{

}

The above code is initialization and the previous one is rather assignment. The initialization really makes sense while you are using some heavy class or containers which does some heavy initializations. Containers will allocate memory of a default number of elements to avoid frequent allocation and de-allocation.

Suppose we have a vector of type int as class member and we are going to initialize it as follows.

CRender( const vector<int>& points )
{
    m_Points = vec; // calling operator= of vector
}

In the above code, the m_Points member will be initialized with default constructor even if didn’t specify that. Inside the body of the constructor, it is calling “operator=” of vector. So for the initialization of a member we have pay more CPU cycles since it will do some allocations, de-allocation or relocation depends on the input. But the following code can save some CPU cycle and thus the code will be a bit faster.

CRender( const vector<int>& points ) : m_pPoints(points)
{
    // Do rest of the processing
}

The above one has a serious drawback while we are using multiple constructors for the object where we need to specify it in the initialization list of every ctors. In that case it can be moved to a common function where you can do the initialization even if you pay more CPU cycles.

So never ever throw exceptions or return from constructors or destructors because it will show some unexpected behaviours because of the partial construction or destruction of objects (members).

 
  • Dilip

    You seem to be advocating some strange rules. Throwing exception from a constructor is a well established way of informing the client code that the object construction did not take place due to some fatal error. In fact it is the only way the caller can ever know if an object was constructed properly.

    Now, whether allocated memory in ctors gets freed properly (before the exception is thrown) is another issue altogether. That part is well covered by Scott Meyers in his More Effective C++.

    The only agreeable point is you SHOULD NOT throw exceptions from a destructor because the dtor might be executing as a part of stack-unwind caused by another live exception. The implementation will just call atexit or something like that and abort the application in such scenarios.

  • http://sarathc.wordpress.com/ S a r a t h

    Dear Dilip,
    In Item 12(Copy all parts of an object ) of effective C++ 3rd Edition it is saying copy all part of an object in copy constructor or operator =. I think it is also applicatioble in the case of construction. i.e initialize all parts of an object. Sorry I was not creating new rules but I was posting an incident which can cause some unexpected behavior.
    IMHO partial initialization of objects are not good. I reffered the name effective C++ because that many people may seem that this is an copied post. that’s why I mentioned that name.

    You are said about destructor is true but IMO, you should INTIIALIZE ALL PART OF AN OBJECT. I know the code I posted is a foolish one but still people making mistakes.

  • cpon

    There is more information about why not throwing exception in constructors and destructors. http://kal-el.ugr.es/~jmerelo/c++-faq/exceptions.html#faq-17.2