Five weird features in C++ that you likely use when you are a great OOP developer

The syntax of C++ seems pretty weird sometimes. Think about the function pointers (int (*ptr) (int, int) = myFunction;), think about functions that return function pointers (int (* func(int a, int b))(int, int)), think about the keyword “const” (const int* const MyClass::GetPointer() const) or about the template syntax. But I were happy when the syntax would be the only problem. The ugly truth, that very often the semantics is more challenging. And why? Because even in some pretty simple cases you need to clearly understand what’s happening in the background.

It is a serious thing. Usually, if you don’t know what’s happening in the background, your program won’t work (and/or you will observe the good old undefined behavior). If you want to handle the behavior of your application, you have to use features that seems pretty weird at the first time. I builded a small collection of such features that I find pretty weird after C# and Java, even if they give short and elegant solutions for common problems. (In some cases, they are the only way to handle the corresponding problems.)

  1. Virtual destructors

    It is banal and everybody knows: a polymorphic base class must have a virtual destructor. If you try to destruct a derived class through the base class pointer and the destructor of the base class is not virtual, then your program won’t find the destructor of the derived class and it leads to a catastrophy in the most of cases.

    Why is that? Just think about. A derived class knows exactly who is its base class or who are its base classes, but a base class never knows which classes are derived from it. So, if we have a base class pointer which points to a derived class and we call the destructor via the pointer, then there is no way to find out the most derived type of the object (and the corresponding destructor) except the C++ virtual function mechanism.

    When the base class destructors are virtual, then the virtual function mechanism will call the destructor of the most derived class and it works correctly even for multiple inheritance, because the destructor of the most derived class will call the destructors of the base classes one by one.

  2. Pure virtual functions with function body

    Is sounds strange, isn’t? If you want to define a function signature without the implementation of the function (because you want to write an interface or an abstract class), then you use the good old “=0” notation, and yes, it means, that the function is pure virtual and you shouldn’t write its implemetnation. Likely you use pure virtual functions because you don’t want to give their bodies. But be careful.

    A pure virtual function doesn’t mean, that the pure virtual function doesn’t have a body. It means, that its class is an abstract class and you can’t instantiate it. It doesn’t mean anything else. You can define the body of a pure virtual function, you can call the implemented pure virtual function with the Base::something() notation even in constructors and destructors. Actually there are some hacks that are based on this strange feature.

  3. Unimplemented member functions

    It is the opposite of the last paragraph. When you add a signature of an ordinary function to the class and you don’t implement it, it causes no problem while you don’t try to call the function. You can instantiate the class, you can use the class, you can use any other parts of the class and it is no problem, that there is an unimplemented function. Pretty strange, don’t you think?

    Using unimplemented member functions is a common way to disable the assignment operator and the copy constructor. Of course if your program try to call an the unimplemented function, it causes problem in compile time and in link time.

  4. The behaviour of user-defined assignment operators in derived classes

    The copy constructor is the same story, but I think constructors have more naturally syntax because of the initialization lists. Every time when you write an assignment operator to a derived class, you have to remember that the user-implemented assignment operator never calls the assignment operator of the base class. Short and sweet, you have to add an extra function call (the call of the base class assignment operator) manually:

    const Derived& Derived::operator=(const Derived& d)
    {
      Base::operator=(d);
      ...
    }
    

    Match it against the syntax of the copy constructor:

    Derived::Derived(const Derived& d)
        : Base(d), ...
    {
      ...
    }
    

    The syntax of the assignment operator is less naturally, so it is easier to forget.

  5. Implicitly inlined member functions

    Did you know, that every function, which implementation is in the definition of its class, is inlined implicitly? Java programmers, who think that it is good idea to put every implementation details between the curly brackets of the class definition, must be very surprised when they suddenly find out that they marked every class member function as inlined.

    Good programmers always decide when they want to inline a function, they don’t mark member function randomly as inlined. So you have to separate the implementation and the definition if you don’t want to inline a function and you have to put the implementation of inlined functions in the header file.

Advertisements

About vrichard86

I'm Richard and I'm an enthusiastic coder. I want to be better and I think a professional journal is a great self improvement tool. Welcome to my blog, to my hq, to my home on the web!
This entry was posted in C++ and tagged , , . Bookmark the permalink.