Jump to content
  • Advertisement
Sign in to follow this  
lordikon

Post-constructor call to an initialization method from base class?

This topic is 2543 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

What I'm trying to achieve is that after my object is finished constructing, the base class makes a call to a virtual function named Initialize(). However the only time I can see to call that method is during the base class' constructor, and if I call it then it will occur before the derived classes constructor has had time to finish, not to mention calling a virtual function during a constructor just seems dangerous, even if it worked the derived class(es) aren't finished initializing. What I'm trying to avoid is having to have all programmers writing derived classes have to remember to call Initialize(), I would prefer that it "just happen" as far as they're concerned.

Here's an example of the flow I would like to achieve.

Class B derives from A

// Programmer only does this
MyObj B = new B();

1.) A's constructor occurs
2.) B's constructor occurs
3.) A calls virtual Initialize() method

// ===============================
// This part occurs if B handles Initialize()
3a.) B receives call to Initialize()
3b.) B calls base class Initialize()
// ===============================

4.) A receives call to Initialize()

Share this post


Link to post
Share on other sites
Advertisement
This is usually done by using a factory function. The factory creates the object and after it's created the factory calls the virtual function. Generally the constructors are all made protected and the factory is friended.

Share this post


Link to post
Share on other sites

This is usually done by using a factory function. The factory creates the object and after it's created the factory calls the virtual function. Generally the constructors are all made protected and the factory is friended.


I've done it this way in the past, I was hoping there was some other way. I might be able to go with a factory this time around as well I guess. I'll have to consider that.



Is there a good reason the constructor itself isn't initializing the object?


I've made it a habit to leave certain initializations out of the constructor for some of my classes. I often have cases where I want to reset an object, or re-initialize it. If I have an initialize method I can call it to reset the object, as well as setup the object when it is first created. I'll usually have constructors call the initialize method.

Share this post


Link to post
Share on other sites
There is another way, though I don't particularly recommend it. Make all your constructors protected as with the factory method and instead friend a template class:

template <typename T>
struct Initializer : T {
Initializer() { initialize(); }
};

Then instead of going Foo * foo = new Foo(); use Foo * foo = new Initializer<Foo>(); Unless you're doing something like comparing typeids directly (which you shouldn't) or similar polymorphism no-nos, it should work pretty much the same as the factory.

Share this post


Link to post
Share on other sites
You could also consider extracting the virtual parts in to a "strategy object". What was the base object would now get given the strategy object at construction time and can call any virtual functions on it, as needed, in order to perform initialization.

Share this post


Link to post
Share on other sites
The other thing you can do use a prototype-style factory object i.e. one that creates an object of a particular type by cloning a 'prototype' object of that type. With this kind of factory, you can use 'the curiously recurring template pattern' to implement the clone() and/or copy() member functions in the base class of your object hierarchy and then get these functions for free without boilerplate in derived classes. Obviously, call the polymorphic initialize() member function in the clone() or copy().

Share this post


Link to post
Share on other sites
Protected keyword has mostly fallen out of favor. Experience has shown it breaks encapsulation and has negative impact on design.

The whole concept of "make it protected but make sure it's only called from factory etc..." shows the problem. It also imposes strict order in that specific method must be called in specific order at specific point, or class invariants aren't upheld.

After call to construction, class must be valid and fully initialized. If some inherited concept exists, it must be public and it may be invoked at any time. Having a reset() function doesn't break this.

Strategy pattern was mentioned. Constructor is one concept, post-initialize is another. By having two independent concepts such design breaks single responsibility rule.

Assuming something like this:class Socket {
Socket();
virtual initialize();
};
class UDPSocket() : Socket {};
class TCPSocket() : socket {};
Construction takes two phases. First one, say, creates socket resources, second one perhaps binds the socket.

Refactoring is somewhat mechanical. We have two individual operations called in sequence. Thereby, the compile-time version:
class SocketResources {};
class SocketBinder {};

template <class Resource, class Binder >
class Socket {
private:
Resource r;
Binder b;
};
typedef Socket<UDPResource, UDPBinder> UDPSocket;
typedef Socket<TCPResource, TCPBinder> TCPSocket;
Alternatively, run-time polymorphism or IoC/DI:class Socket {
Socket(Resource * r, Binder * b) { }
private:
Resource * r;
Binder * b;
}
class UDPSocket : Socket {
UDPSocket() : Socket(new UDPResource(), new UDPBinder()) {};
}
...
Template based version doesn't have a common base class, so different instances cannot be immediately mixed, but at same time doesn't require base classes for composite types.

While it may seem like more code, there is no way getting around the orthogonal inheritance "grid" or "tree". For each additional parameter, number of dimensions increases. By trying to implement them via workarounds in restrictions of C++ type system, they still end up somewhere, most likely in factory method. For two types it's managable - but two types don't really need much of a framework. For anything more, it becomes n^m problem and a design disaster.

Design is linearly extensible (just add more parameters) without increasing complexity. It's also composable - classes with such design can become part of same type of composition without side-effects, due to fully respecting SOLID principles.


Using a factory for such task is inferior option, since instead of using built-in type hierarchy and composition, it requires user to reimplement them.

Share this post


Link to post
Share on other sites

Protected keyword has mostly fallen out of favor. Experience has shown it breaks encapsulation and has negative impact on design.

I've seen that said for protected data, but not the keyword as a whole. Do you have any sources for that being said for protected functions?

Share this post


Link to post
Share on other sites

[quote name='Antheus' timestamp='1325193499' post='4897920']
Protected keyword has mostly fallen out of favor. Experience has shown it breaks encapsulation and has negative impact on design.

I've seen that said for protected data, but not the keyword as a whole. Do you have any sources for that being said for protected functions?
[/quote]
I saw a really nice practical example a while back, but I have no clue where.

Basic idea is that each method that can be invoked outside of your control (inherited classes, callers) needs to preserve invariants, thereby excluding "call in this particular order" scenario.

If something preserves invariants, then it can be called at any time by anyone and becomes public API. Having things like initialize() or reset() for some reason limited to protected serves no design purpose and is merely an implementation detail.

Interfaces accomplish the goal of hiding members by defining the intended public interface while implementation uses different "public" interface. A function operating on IFoo or factory providing IFoo hides all the details while still giving full access to implementors if so desired.

And as pointed above, protected introduces a new soft invariant on how certain members should be used but lacks enforcement.

Only tangential benefit might be IDE code completion scoping to reduce clutter (again interfaces). But clutter is a symptom of complexity, so it can either be reduced, or something is inherently complex.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!