Tuesday, November 25, 2008

c++: virtual constructor

Recently I was asked about an virtual constructor in c++.
The conception of virtual constructor makes me confused.
First of all on the construction stage object doesn't have v-table. The construction process goes from the base class to the current. So current object doesn't know anything about the derived classes on the constructing stage. Even if class could somehow know(if the v-table was available in constructor) it's nonsense to call functions from the derived objects that haven't been constructed. It may cause undefined behavior if those virtual functions work the the data from their own class.
Specially for me, constructor is the place where the object is being initialized - the class members are initialized by given or default values, some actions to prepare object for the further work. There shouldn't be any other calls that can be resolved via v-table(to be concrete - virtual functions). If there are some, and you expect them to be called in the constructor, they probably should be called in a special 'init' virtual method defined by user and exposed in the documentation that it should be called just after the constructor and it should be redefined in derived classes. And only when the object construction is complete the 'init' method should be called.
There are some idioms of virtual constructor you can find in the Internet which suggest to create special virtual method 'construct' that return new copy of the object using 'new' to construct it. It may cause some troubles with memory leaks but to avoid this the smart pointers can be used.
Anyway this idioms hide the initial meaning of the constructor - to construct the object.

Where 'virtual' constructor can be used?
If, say, you have class A, which works with local data. On construction stage you want to connect to the local storage. Later you want to define class B that works with the remote storage. You are trying to connect to the remote storage in the constructor. You have realized, that can do connection to the storage in 'connect' method that is virtual. When you call 'connect' in the constructor you expect that in case of

A *a = new B;
B::connect will be called in the constructor. But how A knows how to perform connection to the remote storage if the connection metadata will be available in constructor of class B that will be called later?
With understanding of concepts of object construction it should be clear that constructors can not be virtual, or better to say they _should_not_ call functions as virtual.
It's much better to expose 'connect' method to make a connection rather hide it in constructor.
The worse can happen if 'connect' throws exceptions. It's not a good idea to throw exceptions in constructor. It's much harder to work with constructors that may throw exceptions.
Usually I don't expect that exception is going to be thrown in constructor, but if it was - the things are really bad and the program probably should finish here. But if I can't connect - well, probably I should wait some time and try to connect later. It's more expressive to put 'connect' into try...catch block than to put
A *a = new B;
there and try to reconstruct object each time the connection failed. This strategy even takes more memory/cpu resources.

No comments: