Exception handling in the list of constructor initializers

In my project, I found a piece of code in which the method was called in the list of constructor initializers.

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

I noticed that there is a chance that Test2 users might pass NULL to the constructor. Since the pointer is used without checking, there is a possibility of access violation.

This made me look at exception handling in the constructor initializer list. I found in one of the articles that they try to use in the list of initializers. I wrote a 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 does not compile in VC6.0 . Do I need to make any changes to compile it in VC 6.0?

In addition, in one of the articles I found that using the list of initializers of the try in constructor does not strictly confirm the C ++ standards. In this case, how do we handle exceptions in the list of constructor initializers (standard processing method)?

Thanks.

+4
c ++ exception-handling
source share
7 answers

First, if you are looking for a standard NULL pointer, C ++ does not guarantee that an exception will be thrown, so your code is useless for this case.

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

Thirdly, constructor / function exception blocks are widely regarded as time-consuming - look at http://www.gotw.ca/gotw/066.htm and other articles on the Herb Sutter GotW site.

+8
source share

Standard section C ++ 15/3

The try-block function binds the handler using the pointer initiator, if any, and the body of the function. the exception that occurred during execution of initializer expressions in ctor-initializer or during the execution of a functional body transfers control to the handler in function-try-block in the same way as the exception that occurred during execution of the try-block transfer is controlled by 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 } 
+11
source share

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

You need to either increase to 7.0, or just perform initialization in the designer body.

+4
source share

Can't you just use the function to check ptr, for example:

 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; }; 
+3
source share

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

Another option, of course, is a reference to the construct that should be connected.

+1
source share

(for fellow travelers)

Another solution if we do not want to keep a copy of the ptr / shared_ptr file:

 class Foo::Pimpl { public: bool paramTest_; Pimpl(ConstNodePtr root) try : paramTest_( root ? true : throw std::invalid_argument("Foo (pimpl) constructed from NULL node")), ... { ... } catch (...) { throw; // rethrow } 
0
source share

There are already many useful answers, but I will try to add a little, maybe this will help someone.

First of all, as others have already noted, dereferencing a nullptr or an invalid pointer (address) does not throw an exception in standard C ++. MSVC supports it through Structured Exception Management , but it is not portable. Read more about this at.

The try block function in the constructor does not allow you to throw an exception, it will be propagated in any case, if you have not chosen another. And when you enter the catch clause, all members of the class are already destroyed. So the only plausible thing you can do about this is to log an error or somehow modify some global variables. That is why he considered it more or less useless.

Regarding your source code

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

you can use the ternary operator to check the zero value of pTest only in the list of initializers and perform some appropriate actions if it is equal to zero; just set m_nDuplicateID to nullptr or some other value depending on its type, call another function and use its return type, etc.:

 Test2(Test* pTest): m_pTest(pTest), m_nDuplicateID( pTest ? pTest->getTestID() : /*some value or call*/ ) { } 

you could use several nested ternary operators to create more complex execution threads.

And just for completeness, this is not the case with your code, but it can carry someone in the same situation. If you used a member of your class m_pTest to initialize m_nDuplicateID, this will depend on the order of these members in the class declaration, since class members in the list of initializers are initialized in the order of declaration, and not in the order in which they appear in the list of initializers, so this can be a problem, and it's better to avoid dependencies on member initialization order:

 class A { A( B* pTest ); int m_nDuplicateID; B* m_pTest; }; A::A( B* pTest ) : m_pTest( pTest ), m_nDuplicateID( m_pTest->someMethod() ) // here m_pTest isn't initialized yet, // so access violation probably { } 
0
source share

All Articles