views:

1281

answers:

5

In my project I found a piece of code in which a method was getting called in constructor's initializer list.

Test2(Test* pTest):m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
    {
    }

I observed that there is a chance that the users of Test2 might pass NULL to the constructor. Since the pointer is used without validation there are chances of Access Violation.

This triggered me to look into exception handling in constructor's initializers list. I found in one of the article that try can be used inside initializer list. I wrote small test program to test this concept:

//Test class stores the unique ID and returns the same with API getTestID
class Test
{
public:

    Test(int nID):m_nID(nID){
    }

    int getTestID() const
    {
      return m_nID;
    }
private:
    int m_nID;

};


class Test2
{
public:

    Test2(Test* pTest) 
     try :m_pTest(pTest), m_nDuplicateID(pTest->getTestID())
    {
    }
    catch (...) 
    {
     cout<<"exception cought "<< endl;
    }

    void printDupID()
    {
     cout<<"Duplicate ID" << m_nDuplicateID << endl;
    }
private:

    Test* m_pTest;
    int m_nDuplicateID;
};

int main(int argc, char* argv[])
{

    Test* pTest = new Test(10);


    Test2 aTest2(pTest);
    aTest2.printDupID();


    delete pTest;

    return 0;
}

This code is not getting compiled in VC6.0. Do I need to make any changes to make it compile in VC 6.0?

Also, in one of the article I found that using try in constructor's initializer list does not strictly confirms to C++ standards. In that case, how do we handle the exceptions in constructor's initializers list (standard way of handling )?

Thanks.

+3  A: 

According to this article, it looks like you just can't do that in VC++ 6.0

You'd either have to upgade to 7.0 or just do the initialization in the constructor body instead.

Eric Petroelje
I'd go with the upgrade, since initialization list sometimes can't be avoided (e.g. to initialize references).
Luc Touraille
+6  A: 

Firstly, if you dereference the NULL pointer standard C++ does not guarantee that that an exception will be thrown, so your code is useless for this case.

Secondly, if an exception were thrown, what would your exception handler do?

Thirdly, constructor/function exception blocks are widely considered to be awaste of time - take a look at this http://www.gotw.ca/gotw/066.htm and other articles on Herb Sutter's GotW site.

anon
Even pTest->getTestID() in the constructor could throw an exception.
aJ
You don't do Herb's article justice: construction exception blocks are to be used only for rethrowing different exceptions, or for side effects such as logging. For those purposes, they certainly aren't a waste of time. They just can't do more than that.
Pontus Gagge
Yeah, don't go there with the constructor exception stuff. Just check the input argument and assert or throw as appropriate.
Brian Neal
+4  A: 

C++ Standard Section 15 / 3

A function-try-block associates a handler-seqwith thector-initializer, if present, and the function-body. An exception thrown during the execution of the initializer expressions in the ctor-initializer or during the execution of the function-body transfers control to a handler in a function-try-block in the same way as an exception thrown during the execution of a try-block transfers control to other handlers.

class C 
{  
    int i;  
    double d;  
public:  
    C(int, double);  
};  

C::C(int ii, double id)  
try  : i(f(ii)), d(id)  
{  
//constructor function body  
}  catch (...)  
{  
//handles exceptions thrown from the ctor-initializer  
//and from the constructor functionbody  
}
Mykola Golubyev
The article in "Eric P"s answer says it is not in standard. That apart, I am wondering why VC 6.0 is not supporting then.
aJ
From the article "You cannot compile this in Visual C++ 6.0, since its does not strictly confirms C++ standards. "
Mykola Golubyev
Thanks. I got confused.
aJ
VC++ 6.0 came out before the standard
anon
+1  A: 

People still use VC6? Seriously, VC6 is hardly a standards-complaint compiler. Do yourself a favor and at least get VS2005. VC6 is your problem. Try VS2008 express and see if it compiles.

The other option, of course, is to take a reference on construction, which needs to be bound.

rlbond
Unfortunately, there are many multi-million line monolithic C++ applications in VC6 which it would cost many developer years to port to modern C++, and many companies don't want to make the switch.
Pete Kirkham
@Pete: these companies don't understand asymptotic growth then. Growth of cost, that is. Maintaining such ancient code doesn't come cheap, and it doesn't get cheaper.
Konrad Rudolph
I agree, but you don't always get enough influence to make the change (particularly as a contractor), and often there are many more pressing issues to deal with.
Pete Kirkham
A: 

Can't you just use a function to check the ptr, e.g.:

template<typename P>
P* checkPtr (P* p)
{
    if (p == 0)
        throw std::runtime_error ("Null pointer");
    return p;
}

class Test2
{
public:
    Test2 (Test* pTest)
        : m_pTest (checkPtr (pTest))
    {
    }

    Test* m_pTest;
};
jon hanson