In this post I bring you back to Scott Meyers book on effective C++ programming. We will consider the next four tips and hopefully add them to our programming arsenal.
What follows is my take on the next four tips:
5: Know what functions C++ silently writes and calls: If you do not specifically declare constructors, destructors and a copy assignment operator, most C++ compilers will generate them for you. The issue here is that you may run into trouble with the way the compiler generates the default functions. If the compiler decides that the generated functions don’t make sense it will silently refuse to generate them. It may also refuse to allow you to define operator functions because of the way the default constructors were created. There are several examples in the book but the bottom line is that compilers implicitly generate a class’s default constructor, copy constructor, copy assignment and destructor so understand what that means to your program.
6: Explicitly disallow the use of compiler-generated functions you do not want: We saw in item 5 that if you don’t declare certain functions that the compiler will generate them for us. If we want to prevent this behaviour we have to declare the functions ourselves. But what if we want to not have the functions at all. The thing to note here is that when the compiler generates functions for us they are public by default. If we want to make sure the functions are not available, we declare the functions yourself and make them private. By declaring the function yourself you prevent the compiler from generating a public version.
7: Declare destructors virtual in polymorphic base classes: C++ specifies that when a derived class object is deleted through a pointer to a base class with a non-virtual destructor, the results are undefined. The example from the book refers to a dynamic class generated by a factory class. The returned class is guaranteed to have the functions of the base class, but it is not the same type. The returned class will be on the heap so it must be deleted in order to prevent a memory leak. If you try to delete the generated class using the pointer, the base class will be deleted but the derived class and its data members will not. To solve this problem we give the base class a virtual destructor. What this does is call the destructor of the derived class when the base class is deleted. Note that declaring a virtual destructor in a class that is not meant to be a base class is not a good idea because there will never be a real constructor to execute.
8: Prevent exceptions from leaving destructors: In order to explain this one Scott provides a clear example. Consider a vector that contains a number of elements. If an expecption is thrown while destroying the first element of the Vector, the other nine elements still need to be destroyed. If an exception is thrown while destroying the second element, now there are two active exceptions and that is one too many for C++ and depending on the conditions, this can cause the dreaded “undefined behaviour”. The bottom line here is that C++ does not like destructors that emit exceptions. There are several mechanisms that we can use to address this issue. If we think that an exception may result in a visit to the land of “undefined behavior” we can exit the program in the destructor. The second and less desirable thing we could do is to swallow and ignore the exception as part of the destructor. This is undesirable because we lose important information about the error, but there may be cases where we need the program to continue executing. The last and perhaps best thing to do with exceptions is to handle them with functions inside the class. Note that in all cases the exception is not allowed outside the class in question.
So there you have the next four tips. My thanks to Scott for providing the raw material for this post and I hope that these tips save you some grief.