Safe way to initialize a derived class

I have a base class:

class CBase { public: virtual void SomeChecks() {} CBase() { /* Do some checks */ SomeChecks(); /* Do some more checks */ } }; 

and derived class:

 class CDerived : public CBase { public: virtual void SomeChecks() { /* Do some other checks */ } CDerived() : CBase() {} }; 

This design seems a little strange, but in my case it is required because CBase does some checks and CDerived can mix some checks between them. You can see this as a way to hook functions in the constructor. The problem with this design is that when building CDerived, CBase is created first, and there is no awareness of CDerived (so that someChecks () overloaded function is not called).

I could do something like this:

 class CBase { public: void Init() { /* Do some checks */ SomeChecks(); /* Do some more checks */ } virtual void SomeChecks() {} CBase(bool bDoInit=true) { if (bDoInit) { Init(); } } }; class CDerived : public CBase { public: virtual void SomeChecks() { /* Do some other checks */ } CDerived() : CBase(false) { Init() } }; 

This is not very safe because I want the constructor with a false parameter to be protected, so only derived classes can call it. But then I will have to create a second constructor (which is protected) and force it to accept other parameters (it may not be used because the constructor is called when Init () does not need to be called).

So, I'm completely stuck here.

EDIT Actually, I want something like this:

 class CBase { protected: void Init() { /* Implementation of Init ... */ } CBase() { /* Don't do the Init(), it is called by derived class */ } public: CBase() { Init(); } // Called when an object of CBase is created }; class CDerived : public CBase { public: CDerived() : CBase() { Init(); } }; 

It seems to me that it is impossible to create 2 constructors with the same arguments that are protected and public?

+4
source share
6 answers

Calling virtual methods in the constructor / destructor is prohibited. Despite the fact that the processes behind this are that the virtual methods invoke the most derived version of the method, and if the constructor did not finish, then most of the data received was not correctly initialized, and therefore this can provide efficiency for using an invalid object.

What you are looking for is a PIMPL design template:

 class CBase { ... }; class CDerived: public CBase { ... } template<typename T> class PIMPL { public: PIMPL() :m_data(new T) { // Constructor finished now do checks. m_data->SomeChecks(); } // Add appropriate copy/assignment/delete as required. private: // Use appropriate smart pointer. std::auto_ptr<T> m_data; }; int main() { PIMPL<CDerived> data; } 
+3
source

What you want is called a two-phase design. C ++ does not offer this as a syntax construct, so you have to do it yourself.

The usual way to do this is to use the programmerโ€™s universal ointment (add another layer of indirection): you transfer your classes to some other class. This class constructor first calls the constructor of your class, and then the extra initialization function. Of course, this ruined your design a bit.

+3
source

You are doing something rather strange here.

The following steps will work and are completely safe:

 class CBase { void SomeChecks() {}; public: CBase() { /* Do some checks */ SomeChecks(); } }; class CDerived: public CBase{ void SomeOtherChecks() {}; public: CDerived() { /* Do some other checks */ SomeOtherChecks(); } }; 

In this hierarchy, when CDerived is built, CBase first executes its SomeChecks (), and then CDerived makes its own OtherChecks (). This is how it should be.

What you did SomeChecks () virtual shows the intention to allow SomeChecks to be completely redefined in derived classes, while these checks still need to be executed in the constructor. This usually indicates ruined architecture; in fact, you are trying to put some knowledge of the derived class in the parent, which is usually not true.

+2
source

There is no clean solution. You cannot safely perform CDerived functions until the CDerived ctor body is entered. At this point, CBase ctor must have returned.

The workaround may be as follows:

 protected: CBase(boost::function<void(*)(CBase*)> SomeChecks) { // Base checks SomeChecks(this); // Checks provieded by derived ctor but running on CBase member. } 
+1
source

It is necessary to create one class hierarchy so that only the base class is responsible for checking the basic constraints. This can be a problem when, for example, the child class must invent some constructor arguments for the constructor of the parent class.

Another problem that calls another constructor with the same argument signature can be solved with the "tagged constructor" trick: create a template constructor.

 struct C { enum eConstructor { cCheck, cNoCheck }; template< eConstructor constr = cCheck > C( int i ); int positive_value; }; template<> C::C<C:: cCheck >( int i ) : positive_value( std::max( 0, i ) ) { } template<> C::C<C::cNoCheck>( int i ) : positive_value( i ) { } struct CFive : public C { CFive(): C<C::cNoCheck>(5) {} }; 
0
source

Maybe this will work for you:

 class CBase { public: CBase() { /* Do some checks */ SomeChecks(); /* Do some more checks */ } virtual ~CBase(){} /*don't forget about this*/ virtual void SomeChecks() {} }; class CDerived : public CBase { public: void SomeChecks() { //without virtual /* Do some other checks */ CBase::SomeChecks(); //if you want checks from CBase } CDerived() : CBase() {} }; CBase* fromBase = new CBase(); //checking with CBase::SomeChecks() CBase* fromDerived = new CDerived(); //checking with CDerived::SomeChecks CDerived* alsoDerived = new CDerived(); //checking with CDerived::SomeChecks 
-one
source