“Effective C++” : Part 3

With this post I circle back to the next four tips from Scott Meyer’s book on effective C++. This post brings us to the end of chapter 2 in the book. These next four tips are not as obscure as some of the previous ones, so I expect this will be a quick read.

9: Never call virtual functions during construction or destruction: This rule has to do with the way derived classes are evaluated and executed. Unlike Java or C#, C++ executes base class constructors before the constructors of derived classes. If data members from the derived class are used in the virtual function they will not have been initialized and we know where that will lead….”undefined behaviour”.

In actual fact during the base class construction of a derived type, the type of the object is that of the base class. This means that things like “dynamic_cast” and “typeid” will treat the object as the base class type. An object doesn’t become a derived class object until execution of the derived class constructor begins.

If a derived class object has multiple constructors where similar work had to be done in each, a programmer may move the common code to a private non-virtual function called “init”. If the base class also implements the function (ie it is over loaded in the derived class), then during construction, the base class version will be called instead of the derived class version.

The bottom line is as stated above, “Don’t call virtual functions during construction or destruction”.

10: Have assignment operators return a reference to *this: This issue is more of a coding standard issue than a rule. One of the interesting things about assignments is that you can chain them together. Assignments are right-associative which means that:

x=y=z=15 is evaluated as x=(y=(x=15));

The way this is implemented is that assignment returns a reference to the left-hand argument. This is only a convention, but it is the way string, vector, complex and many of the other parts of the standard library work. If you don’t have a good reason to change it, you can avoid future problems by going with the flow.

11: Handle assignment to self in operator=: To explain the issue here we are going to use and example. It is not always obvious when an object is being assigned to itself so it is important that you handle this case.

class thing2{…};

class thing1{

private
thing2 * t2;
}

thing1&

thing1::operator=(const thing1& rightSide){
delete t2;
t2 = new thing2(*rightSide.t2);
return *this;
}

So this issue here is that rightSide and thing2 are the same thing. The first thing we do is to delete the old thing2 which deletes the new thing2 at the same time. a pointed to a deleted object gets assigned to thing2 which will not end well. There are several things that can be done to make this end better. we can add a check to see if the objects are the same thus avoiding the delete or we can change the order to the statements to insure both safe for assignment and exceptions. Consider the following:

thing1::operator=(const thing1& rightSide){
thing2 *tempThing = t2;
t2 = new thing2(*rightSide.t2);
delete tempThing;
return *this;
}

Now if “new thing2” throws an exception t2 remains unchanged.

12: Copy all parts of an object: In tip number 5 we discovered that copy functions are generated by the compiler if we don’t define them ourselves. Scott points out that compilers tend to be insulted if you declare your own versions and they retaliate by not telling you when your versions are most likely wrong. If you have a class with multiple data members and your copy function misses one of the data members, the compiler will assume that you did this on purpose and will not even generate a warning. One of the most obscure ways that problems arise is through inheritance.

If a class inherits data members from a base class they must be specifically copied in the copy function of the derived class. Programmers will often copy the derived classes data members and overlook the base class data members. Once again, the compiler thinks that you did this on purpose and will not bother you with a warning.

The bottom line here is pay close attention to what you are doing if you decide you have to implement your own copy functions.

That’s it for today. You can take the rest of the day to review your code and apply the changes [grin]. I hope you’re learning as much as I am from these posts and comments are always welcome.

This entry was posted in General and tagged , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *