Sign in to follow this  
jwein

Initialize in constructor or in a separate function?

Recommended Posts

Hello, I would like to ask you a suggestion about generic programming and software architecture. Do you think it's better to initialize an object in its constructor or in a separate Initialize() method of the class? If I have, for example, a Renderer class, which represents a wrapper for the Directx API, should I initialize Directx in the constructor or in a separate function? Is there a generic rule to choose between the two? Thank you :-) [Edited by - jwein on March 19, 2010 3:22:47 PM]

Share this post


Link to post
Share on other sites
In the constructor. That's what it is there for.

There may be rare cases where this is not appropriate and you need to provide for deferred initialization. I find those cases to be extremely rare in otherwise well-designed systems (they may be less rare in poorly designed systems). So... in almost all cases, the constructor is where object initialization logic should be.

Share this post


Link to post
Share on other sites
You can only initialize in the constructor, and in C++ you can only initialize in the initializer list:

struct Foo {
const bool hello;
int world;
int how_is_it;

Foo () : hello(true), world(0xDeadFeed) // initialization
{
how_is_it = 42; // assignment
}
};


It often yields the same results, thanks to optimization. But some entities, like the const-bool in the example, can only be initialized, not assigned. Initialization is always prefereable, sometimes to the degree that you should better write static helper functions instead of doing assignment (E.g.:

struct Foo {
const bool hello;

private:
static bool aComplexDecision(int x) {
...
}

public:
Foo (int x) : hello(aComplexDecision(x)) {}
};

Share this post


Link to post
Share on other sites
Quote:
Original post by jwein
Thank you for the answer. Can I ask you if there is a particular motivation or advantage in initializing in the constructor?


There are a few good reasons for this. First, think about logical state. An object doesn't exist until it is constructed. If you initialize during construction, you ensure that the object doesn't start out in an invalid state, so it reduces the chances of bugs.

This is also a very useful paradigm for multithreaded code. If you have an object shared between threads, there's no chances of two different threads accessing the object before it is initialized if you initialize during construction, because you can't reference the instance until after.

Share this post


Link to post
Share on other sites
If you place initialization code in a separate function, then there is a time between object construction and calling this function. During this time, the object is in a kind of intermediate state, that is, it is not usable. If you avoid this, your code becomes cleaner and simpler.

Share this post


Link to post
Share on other sites
I know, but I was referring to the initialization of the class from another point of view.

For example I didn't know where to call D3D10CreateDeviceAndSwapChain, which is supposed to initialize some members of the class, and so on...

Share this post


Link to post
Share on other sites
I like the option for both.

Consider this example from the standard library.

File streams can be created with no parameters. In this case it creates an object that is an unopened stream. You can call open() directly on it later. You also have the option of calling close() on it and then open() with a different file.

They also have a constructor that takes a file name. In that case you get an opened stream. If there is a problem opening the file, however, you must deal with it at construction time. The destructor is similar: If the file is still open the destructor will close it, but if there is a problem writing the last little bit of the file, you won't know about it because destructors don't/shouldn't throw.


If your code requires that kind of initialization in construction, it can make for slow constructors. If your constructor can throw exceptions, it can make for complicated logic. If you don't provide a default constructor you cannot use the class in a container.

Share this post


Link to post
Share on other sites
Quote:
Original post by frob
If you don't provide a default constructor you cannot use the class in a container.

Of course you can, std::vector does not require default construction for example.

Share this post


Link to post
Share on other sites
Quote:
Original post by frob
I like the option for both.

Consider this example from the standard library.

File streams can be created with no parameters. In this case it creates an object that is an unopened stream. You can call open() directly on it later. You also have the option of calling close() on it and then open() with a different file.

They also have a constructor that takes a file name. In that case you get an opened stream. If there is a problem opening the file, however, you must deal with it at construction time. The destructor is similar: If the file is still open the destructor will close it, but if there is a problem writing the last little bit of the file, you won't know about it because destructors don't/shouldn't throw.


If your code requires that kind of initialization in construction, it can make for slow constructors. If your constructor can throw exceptions, it can make for complicated logic. If you don't provide a default constructor you cannot use the class in a container.


I hear what you're saying, but this may not be the best way to phrase this advice. A slow constructor isn't necessarily a problem, since it's not like you weren't going to initialize anyway, and it's often faster than the alternative. Throwing exceptions from constructors is perfectly fine and recommended. A constructor that can throw exceptions will prevent the object from being created. That actually simplifies error handling logic in many cases (Disclaimer: as long as it definitely an error and an exceptional case).

There are workarounds for the default constructor problem, such as using a ptr container (boost), when necessary.

Share this post


Link to post
Share on other sites
You should always construct objects inside their containing class's initialiser list if it's possible to do so. Otherwise both the default constructor and the destructor will be called before anything meaningful even happens. This can be a pretty big performance hit if the object contains other objects which allocate memory, resources, etc. And if you choose to provide a default constructor that leaves the object in an uninitialised state then you run the risk of introducing bugs.

Share this post


Link to post
Share on other sites
Not sure what you'd want to gain from a virtual initialize() function, as it would only ever be called in a context where the dynamic type of the object is known at compile-time. Perhaps you're thinking of the virtual constructor idiom? That doesn't require initialization outside the constructor, though.

Share this post


Link to post
Share on other sites
Quote:
Original post by jwezorek
One case I can think of in which you would want to defer initialization is if you want to make the initialize() function virtual i.e. you can't have a virtual constructor.


True that you can't, but what purpose does that serve? The constructor has to call a base constructor, either implicitly and explicitly, anyway. Unless you're saying all of the base constructors don't apply to how you want to create the object, but then I'd be concerned that you might have an improper object hierarchy.

Can you give an example of when you've found this useful?

Share this post


Link to post
Share on other sites
I was thinking in this moment about the case you suggested.

Should I initialize in the constructor even if the class derives from an interface?

I have, for example, an abstract class Effect, which represents a wrapper for the Directx effect class. The abstract class (interface) requires the filename of the .fx effect file while the derived api-specific D3D10Effect class requires two directx objects, too. I can't obviously define a virtual constructor.

What should be the best way to go?

Share this post


Link to post
Share on other sites
Quote:
Original post by jwein
I was thinking in this moment about the case you suggested.

Should I initialize in the constructor even if the class derives from an interface?

I have, for example, an abstract class Effect, which represents a wrapper for the Directx effect class. The abstract class (interface) requires the filename of the .fx effect file while the derived api-specific D3D10Effect class requires two directx objects, too. I can't obviously define a virtual constructor.

What should be the best way to go?



class Effect
{
public:
Effect(const std::string& p_effectFile) :
... initializer list ...
{
... some creation logic ...
}
};

class D3D10Effect : public Effect
{
public:
// Calling the DirectX objects DXObject for short, don't know what you're actually using :)
D3D10Effect(const std::string& p_effectFile, const DXObject& p_object1, const DXObject& p_object2) :
Effect(p_effectFile), ... other initializer list code ...
{
... creation logic ...
}
};





Edit: Now having said that, I realized it's possible you may be referring to a object cloning or factory mechanism. Let me know if this is the case.

Share this post


Link to post
Share on other sites
The only reason to not put initialization in the constructor is because you don't want to use exceptions.

The deferred initialization argument is IMHO begging the question. There's very likely to be some point in which you have to make the choice of doing initialization in a constructor or not. Deferred initialization changes where that point is in time but doesn't change the fact that you have to make the decision.

Hopefully this thread won't devolve into yet another debate about the merits of exceptions. In the case where you don't want to use them, my preferred pattern is to have a factory method that does the (private) creation and initialization in one step. Requiring the client to do the intialization themselves basically guarantees that at some point somebody will forget and try to use an uninitialized object. Or alternatively, it requires the object to be constantly checking "am I initialized?" before it does anything which is tedious as well as being equally error-prone.

Share this post


Link to post
Share on other sites
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...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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;
};



Share this post


Link to post
Share on other sites
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 failed
Foo * 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 failure
bool 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) // error

Foo * foo = create();
if (!foo) // error

Foo foo;
if (!create(foo)) // error

Foo 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, ....

Share this post


Link to post
Share on other sites
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 :-)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this