Initialize in constructor or in a separate function?

Started by
24 comments, last by jwein 14 years ago
Quote:Original post by jwein
Rydinare, your example is very similar to what I was saying, but I define interfaces as classes which don't have any member variables, so I couldn't provide constructors in the base class because I don't have a string member in the base class but only in the derived ones...


Ahh you said abstract class in that last paragraph, so I didn't realize you meant interface.

If you're derived from an interface and all members are in the derived class, what's stopping you from initializing everything in the derived constructor? I guess that is the part I don't understand.
Advertisement
Quote:Original post by Rydinare
True that you can't, but what purpose does that serve? [ ... ] Can you give an example of when you've found this useful?


(In this case initialize() isn't actually virtual but it's the same idea)
Say you had some base class that is a wrapper around a container class and your going to have derived classes that fill in the container in their constructors. But there's a lot of similarity in the data they're going to fill in: like the beginning and the end of the content are tags saying what kind of container it is, or something. If you move initialization out of the constructor you could do something like:
class BaseClass {   protected:      std::vector<ContentType> _contents;   public:      BaseClass(){}      [ ... ]      void initialize() {         _contents.push_back(getStartTag());         insertContent();         _contents.push_back(getEndTag());      }      virtual ContentType getStartTag() = 0;      virtual ContentType getEndTag() = 0;      virtual void insertContent() = 0;}class DerivedClass : public BaseClass {    [...]    ContentType getStartTag() {return START_FOO;}    ContentType getEndTag() {return END_FOO;}    virtual void insertContent() {       _contents.push_back(...);       _contents.push_back(...);       ...    }}

and then maybe have a public static member function in BaseClass like this
template<typename T> static T* create() {     T* obj = new T();     obj->intialize();     return obj;};



I like to claim that operator new, or object construction is a Factory pattern. This is almost guaranteed to cause a lot of noise...


But let me elaborate:
// Returns instance of foo, or null if creation failedFoo * factory(Parameters ...);


Clean, simple, doesn't require exceptions, is perfectly safe - object is created fully valid - or not at all.

Equivalent using RAII (in any language):
class Foo {  Foo(Parameters) {    // throw on error  }}
Here, Foo is either created fully valid, or not at all. The RAII constraints are met - object acquisition also implies initialization. Destruction is not needed for sake of correctness (but not necessarily system constraints or optimization) in post-C++ languages, in C++ it is handled properly as far as RAII goes.


What if exceptions are not available, or scope of object is managed elsewhere:
// initializes t, returns true on success, false on failurebool factory(Target & t);
This works with auto-allocated objects, or instances. It is also transferrable to procedural languages. Exceptions can be used as well.


While it is now possible to argue semantics of exceptions, "safety", etc... none of those are really relevant for the actual problem at hand. Object construction needs to satisfy the following two goals:
- Act of initialization has pre-condition of undefined state, and post-condition of fully initialized and valid instance
- User must never be left with instance where post-condition holds

This means there are the following scenarios:
try {  Foo foo();  // or  Foo * foo = new Foo();  // of   Foo * foo = create();} catch (InitializationException e) {}


Alternatively:
Foo * foo = (Foo *) malloc(...);if (!foo) // errorFoo * foo = create();if (!foo) // errorFoo foo;if (!create(foo)) // errorFoo foo;if (!create(&foo)) // error


This covers effectively all possible cases, highlighting different tradeoffs. C++ new is not included since non-exception versions would require non-throwing new, which cannot be specified explicitly in code. This means that new would be covered under create() version, ensuring the creation post-condition.

Initialize() member functions are also not included, since they are just a different version of create(foo), where foo is passed explicitly. Even more - initialize member functions do not satisfy the pre-condition. They might require vtable or even something more complex which we cannot guarantee. Pre-condition states that object, even if present is some form, is in undefined state. This requires breaking initialization into two parts, such as new + initialize which is very undesirable.

Whether return values are checked or not is also not a big issue. Lack of checking can be automatically tested as part of compilation process. Such interface will be non-C++ anyway, so the syntax is simpler to parse. But it can be fully automated and cause the build to fail.

But as far as design goes - they are all exactly the same. A Factory pattern. Put in a bunch of keys, get back either an instance, or nothing. Everything else is just a trade-off depending on run-time environment, compiler, APIs, performance, ....
Ah, it's true. I should have said interface and not abstract class :-)

Quote:
If you're derived from an interface and all members are in the derived class, what's stopping you from initializing everything in the derived constructor? I guess that is the part I don't understand.


It's because I tought it would have been more correct to provide a sort of template to let people write the derived classes, because parameters for the pure virtual functions are specified, but it's not the same for the parameters of constructors.

However maybe I have a wrong idea and the constructors of the derived classes don't have to necessarily present the same parameters. My project is still growing up and I don't really know the kind of problems I'm going to meet :-)
Quote:Original post by jwein
Ah, it's true. I should have said interface and not abstract class :-)

Quote:
If you're derived from an interface and all members are in the derived class, what's stopping you from initializing everything in the derived constructor? I guess that is the part I don't understand.


It's because I tought it would have been more correct to provide a sort of template to let people write the derived classes, because parameters for the pure virtual functions are specified, but it's not the same for the parameters of constructors.

However maybe I have a wrong idea and the constructors of the derived classes don't have to necessarily present the same parameters. My project is still growing up and I don't really know the kind of problems I'm going to meet :-)


Ahh, I see what you're getting at. I remember thinking of virtual functions that way a long time ago. But really, it would be better to think of virtual functions as ways to implement polymorphism, not so much as a cookie sheet for what methods a derived class should implement. Just my two cents.
Ok thanks. I'm understanding a lot of things reading this forum.

This topic is closed to new replies.

Advertisement