I want to ask you for your best practices regarding constructors in C++. I am not quite sure what I should do in a constructor and what not.
Should I only use it for
delete this
or the destructor in the constructor.From The C++ Programming Language:
The use of functions such as
init()
to provide initialization for class objects is inelegant and errorprone. Because it is nowhere stated that an object must be initialized, a programmer can forget to do so – or do so twice (often with equally disastrous results). A better approach is to allow the programmer to declare a function with the explicit purpose of initializing objects. Because such a function constructs values of a given type, it is called a constructor.
I usually consider the following rule when designing a class: I must be able to use any method of the class safely after the constructor has executed. Safe here means you could always throw exceptions if the object's init()
method has not been called, but I prefer to have something that is actually usable.
For instance, class std::string
might not allocate any memory when you use the default constructor because most methods (i.e. begin()
and end()
) would work correctly if both return null pointers, and c_str()
does not necessarily return the current buffer for other design reasons, therefore it has to be prepared to allocate memory at any time. Not allocating memory in this case still leads to a perfectly usable string instance.
Conversely, use of RAII in scoped guards for mutex locks is an example of a constructor that may execute for an arbitrarily long time (until the lock's owner releases it) yet is still commonly accepted as good practice.
In any case, lazy initialization may be done in safer ways than using an init()
method. One way is to use some intermediate class that captures all parameters to the constructor. Another is to use the builder pattern.
You MAY throw from a constructor, and it is often the better option than creating a zombie object, i.e. an object that has a "failed" state.
You should, however, never throw from a destructor.
The compiler WILL know what order the member objects are constructed - the order they appear in the header. The destructor will however not be called as you said, which means if you are calling new multiple times within a constructor you cannot rely on your destructor calling the deletes for you. If you put them into smart pointer objects that is not a problem as these objects will be deleted. If you want them as raw pointers then put them temporarily into auto_ptr objects until you know your constructor will no longer throw, then call release() on all your auto_ptrs.
Complex logic and constructor do not always mix well, and there are strong proponents against doing heavy work in a constructor (with reasons).
The cardinal rule is that the constructor should yield a fully usable object.
class Vector
{
public:
Vector(): mSize(10), mData(new int[mSize]) {}
private:
size_t mSize;
int mData[];
};
It does not mean a fully initialized object, you may defer some initialization (think lazy) as long as the user does not have to think about it.
class Vector
{
public:
Vector(): mSize(0), mData(0) {}
// first call to access element should grab memory
private:
size_t mSize;
int mData[];
};
If there is heavy work to be done, you might choose to proceed with a builder method, that will do the heavy work prior to calling the constructor. For example, imagine retrieving settings from a database and building a setting object.
// in the constructor
Setting::Setting()
{
// connect
// retrieve settings
// close connection (wait, you used RAII right ?)
// initialize object
}
// Builder method
Setting Setting::Build()
{
// connect
// retrieve settings
Setting setting;
// initialize object
return setting;
}
This builder method is useful if postponing the construction of the object yields a significant benefit. From example if the objects grab a lot of memory, postponing the memory acquisition after tasks that are likely to fail may not be a bad idea.
This builder method implies Private constructor and Public (or friend) Builder. Note that having a Private constructor imposes a number of restrictions on the usages that can be done of a class (cannot be stored in STL containers, for example) so you might need to merge in other patterns. Which is why this method should only be used in exceptional circumstances.
You might wish to consider how to test such entities too, if you depend on an external thing (file / DB), think about Dependency Injection, it really helps with Unit Testing.
I would rather ask:
What all to do in the constructor?
and anything not covered above is answer to the OP's question.
I think the one and only purpose of the constructor is to
initialize all the member variables to a known state, and
allocate resources (if applicable).
The item #1 sounds so simple, yet I see that to be forgotten/ignored on regular basis and only to be reminded by a static analysis tool. Never underestimate this (pun intended).
Ideally, you should have no code in your constuctors, ever (aside from attribute assignment). There is one important reason: It prevents the composition of objects and makes them un-extensible.
Here is my blog post about this: Constructors Must Be Code-Free