I am still quite new on here so I hope I am posting in right forum.
I am currently writing a small library where I realized I could use some kind of design pattern which lets one pass constructor arguments to initialization or allocation functions, and these can be further specialized in inherited classes.
Something like this:
class MyArgs{
// basically just a wrapper/container
// just a constructor and variables
// everything public or friend of MyClassBase
};
class MyClassBase{
MyClassBase(MyArgs a){
doAllocation(a);
doInitialization(a);
}
//... defining default doAllocation() and doInitialization() ...
};
class MyClassDerived : public MyClassBase{
MyClassDerived(MyArgs a) : MyClassBase(a){}
//... override doAllocation() and doInitialization() ...
};
Does there exist some kind of design pattern which makes this easier?
-
2Note that in C++ (unlike in some other languages), virtual functions behave completely differently when called from a constructor compared to when called after construction has completed.Bart van Ingen Schenau– Bart van Ingen Schenau2020年01月08日 12:34:21 +00:00Commented Jan 8, 2020 at 12:34
-
@BartvanIngenSchenau Aha! That does indeed sound quite important! It does seem to work on my experiment with g++. But maybe I don't know where to look.mathreadler– mathreadler2020年01月08日 13:47:51 +00:00Commented Jan 8, 2020 at 13:47
4 Answers 4
This will not work in view of C++ object lifecycle and the construction logic. Here is what happens:
- you construct a
MyClassDerived
- Before the (empty)
MyClassDerived
constructor body is invoked, first the base object is constructed and then the member variables (with their default constructor unless you use a mem-initializer). - The
MyClass
constructor is therefore invoked at a time there is no completeMyClassDerived
yet. So it doesn’t call the overridden functions, but the original ones of the baseMyClass
class. - The additional
MyClassDerived
members are then constructed. At this point only will theMyClassDerived
object starts to exist and the overridden function be active. Unfortunately they are no longer called at this stage in your code. - Then the body of the
MyClassDerived
constructor is executed.
If you want your approach to work, you need to implement a two phase initialisation:
- first the constructor creates the object, ensuring the minimum initializations
- once the object is constructed, the first thing that is to be done is to invoke an initialising function. This would then properly invoke the overridden function, even if it is called from a function defined for the base class.
This pattern is cumbersome and error prone, since it is easy to forget the initialisation call in the constructing context (not to speak about unexpected copy construction or default construction).
If you nevertheless intend to use this pattern, then you should:
- either make the constructor private and limit the access to the constructor using the builder pattern. This pattern is specially meant for a multistep construction process. Alternatively you could think of a factory method that would also be in good position to ensure proper two phase initialisation.
- or consider implementing a state machine like design, in which the initialisation step would update the object’s state to a usable one. Maybe you could even let the object check the state at each function call and automatically perform second phase of initialisation if it wasn’t done before. But this, although it is convenient for the using context, would be an overhead on every call.
-
Thank you for the answer. Yep, I realized the first part when I experimented, so I made a new constructor for the derived which calls it's own doAllocation(). It works, but is much uglier.mathreadler– mathreadler2020年01月08日 15:37:52 +00:00Commented Jan 8, 2020 at 15:37
There is the Factory Method pattern. Other Creational Patterns may help as well.
-
2You need to provide a more substantial answer than just a pair of links. Consider adding some narrative about each pattern, and why you think those patterns address the original question.BobDalgleish– BobDalgleish2020年01月08日 14:42:22 +00:00Commented Jan 8, 2020 at 14:42
I'm not an expert in C++ and I do not know what aspect you want to simplify. But I guess you could use some factory classes in order to pull out the initialization logic from your classes:
-
You don't need factory classes in C++, there are free (template) functionsCaleth– Caleth2020年01月08日 15:33:36 +00:00Commented Jan 8, 2020 at 15:33
-
This is great! Actually quite close to how I ended up making a minimal example.mathreadler– mathreadler2020年01月08日 20:49:29 +00:00Commented Jan 8, 2020 at 20:49
Thank you very much for feedback, everyone.
Based on answers I came up with a minimal example that works for me.
I suppose we can call it a kind of combination of multi-stage construction and Factory.
template<class C, class Cargs>
class ArgFactory{ // Here is factory. The class C shall have C(Cargs a)
// and define a doAllocate(Cargs a)
public:
static C* Create(Cargs a){
C* lRet = new C(a);
lRet->doAllocate(a); // This is now run after actual construction.
return lRet;
}
virtual void doAllocate(Cargs a) = 0; // This overrideable.
};
class IntegerArgs{
public:
IntegerArgs(int a) : m(a){}
int m;
};
class Integer : public ArgFactory<Integer,IntegerArgs>{
public:
Integer(IntegerArgs a){}
virtual void doAllocate(IntegerArgs a){
m=a.m;
}
int m;
};
Explore related questions
See similar questions with these tags.