12 December 2011

Prohibiting inheritance

Sometimes we need some specific classes, which are better not used in other contexts but this very which we are trying to implement, so we need to make this class non-inheritable, i.e. no class can be derived from this one. To achieve this goal, we need to make the objects of the derived class non-constructible or, even less, non-destructible. The concept 'non-constructible' has more to care about than 'non-destructible', as the latter is directly connected to the only destructor of the class, and the former can be achieved only limiting all the constructors of the class, which also can be added through time. The only way we can prohibit access to some functions are access specifiers, of which only private makes sense here, as public and protected are always accessible by child classes. So now we came to making the destructor private. But in this case we cannot create an object as we cannot destruct it later. One thing we can do just to provide a static member which dynamically creates an object on a heap and returns the pointer. But as soon as we attempt to delete this pointer we will get a compile error again, as it will also try to call the destructor, which is private.

    To cope with this situation we can derive our class from another class, which in its turn has a private destructor, and declare our class as a friend of it, so that the destructor of our class will be able to call the destructor of the pivot class.

struct Helper
{
private:
    friend class Leaf;
    ~Helper();
};

class Leaf : public Helper
{
};

Now the destructor of Leaf has access to the destructor of Helper, but so do the derived classes of Leaf, as they also access the ~Helper() through ~Leaf(). To break this link we can inherit the Leaf from Helper virtually, in that case the most derived class will access the virtual base directly and will not have access.

struct Helper
{
private:
    friend class Leaf;
    ~Helper();
};

class Leaf : virtual public Helper
{
};

If you want to provide a common mechanism for any class, you better not inherit all such classes from the same Helper, as it is logically incorrect to link irrelevant classes in the same hierarchy, especially as some concrete types of more abstract one. So the helper classes should be different. Here we can take advantage of CRTP (curiously recurring template pattern), which creates a generic interface within different base classes for each derived class.

template <typename T>
class NonInheritable
{
private:
    ~NonInheritable();
    friend class T;
};

Now whenever you need to make a class non-inheritable you derive it from NonInheritable<T>, i.e.

class MyClass : virtual public NonInheritable<MyClass>
{
...
};

You can also inherit privately to keep this approach as an implementation detail, though MyClass is NonInheritable, so the inheritance logically should be public (as Stroustrup suggested use public inheritance in case of is-a relationship and a private one in case of has-a).

C++11 introduces a new keyword - final, which makes this peace of code redundant. So if you don't want any other class to inherit from your class, you just specify your class as final:

class NonInheritable final
{
    // class definition goes here
};

now any trial of inheritance will bring to a compile error.

10 December 2011

Inheriting from STL containers

    Recently I had a problem with some smart pointers and containers and needed to test something. Namely, some smart pointers to objects were kept in an STL container and there were also some references to these objects from outside of these containers. So at some moment the objects got deleted and the references became invalid. This very reason I wanted to verify to happen. So I just thought to keep my own container and make it do nothing when removing elements from it, so that the objects never get deleted.

    The first idea come to my mind was to inherit my container from std::map, redefine only erase and clear functions leaving the bodies empty. This should have been a public inheritance, as my container also would have been an std::map. There were surely other choices, like keeping an std::map member and providing my own interface, or inheriting privately to get use of implementation. But my purpose was just to test, so the easiest possible way was relevant.

    What was surprising to me that many of my colleagues told that it is not recommended to inherit from STL containers and, generally, classes. I really did not find any convincing reason not to inherit from std::map, but I found many similar opinions on the internet with the following reasonings:

  • STL containers do not have virtual destructors, so polymorphism cannot be supported
  • One can hardly support all the prerequisites an STL container should suffice
  • Adding/modifying some large stuff can cause algorithms do not work as expected or even do not work at all
    The last two reasons are actually somewhat subjective, so if you have a good understanding of what an STL container is and how it should behave you would hardly break something in the inherited one. As for the first one, I agree, it puts some limitations, but this does not mean that you should never inherit your own class. Here are some points which are better to be kept in mind when inheriting from an STL container:
  • Make the scope of your class minimal
    - There are so many tailored containers that you shall hardly need some new for a user interface, probably that would be needed for an internal implementation, so you can keep it in some internal namespace, and take care of all uses.
  • Do not change the basic logic of the class, if you need some more, inherit privately or keep a data member
    - If the function erase(Iterator) clears the container and insert(const value_t&) removes an element, then you did not actually need an STL container at all.
  • Make your class non-inheritable
    - You can take care of your own class, even you can describe the non-polymorphic behavior in documentation, but what if some time later someone decides to inherit from your class. This is why I prefer to make our class non-inheritable and not worry about the descendants (read the next article to find out how to make a class non-inheritable).
  • Create a member typedef in the class which is using your class, so that you never specify your class type directly, i.e.
    class object
    {
    public:
        typedef MyContainer<object> container_t;
    ...
    }; 

    - Now if you have to create a container you don't care about the underlying type of container and you can make  object use another container only by changing the typedef line. Even a better practice would be to create an object_traits template class and then inherit the object from it, like this:
    template <typename T>
    struct object_traits
    {
        typedef MyContainer<T> container_t;
        typedef T object_t;
        typedef T& ref_t;
        typedef T* ptr_t;
        .....
    };

    class object : public object_traits<object>
    {
        ...
    };
So, in the end, go ahead and inherit from any class you want if you really find that you need that. If you inherit and come to some difficulties it means you shouldn't have done that, you have something designed incorrectly.

9 December 2011

Maker functions help in type deduction

    One good feature of C++ compilers is automatic implicit type deduction for template functions, i.e. retrieving template argument types from the passed formal parameters.

template <typename T>
void do_something(T arg)
{
    std::cout<<arg<<std::endl;
}
...
do_something(1); // T = int
do_something(1.0); // T = double

   Though this feature is not supported for classes, and this is logical, as we can have various versions of constructors, and if we create some objects of the class without explicitly specifying the template type the type could not be deduced correctly, e.g.:

template <typename T>
class Container
{
public:
    Container(const T& elem, int cnt = 1); // creates container
                                  // with 'cnt' copies of 'elem'
    Container(int cnt); // creates a container with one T() element
    Container(const Container<T>& other); // copy constructor
...
};
...
int a = 5;
Container c(a); // which constructor to call : first or second ???


To avoid every time specifying the concrete types for templates, we can create a helper function, making use of implicit type deduction of function template arguments. So now we can write


template <typename T>
Container<T> make_container(const T& elem)
{
    return Container<T>(elem);
}

Now we can call make_container without worrying about the type, just passing the object to be kept in container. But this would be useful only we need to pass an object to another function, otherwise we should specify the concrete type to declare an object of class Container<T>. This technique is much used in STL and boost libraries (e.g. std::make_pair in <utility>). It can be helpful if your type T is some complex expression, for instance a pointer to template function taking as arguments several objects of other complex types etc.


21 October 2011

Conditional initialization of 'const' variables

Declaring a variable 'const' if one is not going to change its value is a good practice, even more, not declaring it 'const' is a bad practice and may sometimes be error prone. The question is "how can we initialize a 'const' variable with different expressions based on some condition evaluation?". Let us have 'const' variables somewhere in our code initialized with a function call or just a constant expression:

const int val = 5; // initializing with const expression
const MyClass &ref = get_object_of_my_class();
// returning an object or a const reference
MyClass *const ptr = get_pointer();
// returning a const or non-const pointer

Now consider some piece of code has been changed and we need to update our part so that the previous behaviour is relevant only if some condition applies. Let's assume that the condition expression is evaluated and the value is returned by the following function:

bool condition_applies(); // the definition does not matter

So now the part of old code should look like this:

if (condition_applies()) {
    const int val = 5;
    const MyClass &ref = get_object_of_my_class();
    MyClass *const ptr = get_pointer();
}
// But for the new case when condition does not
// apply we shall have an else statement
else {
    const int val = 1;
    const MyClass &ref = get_another_object_of_my_class();
    MyClass *const ptr = get_another_pointer();
}


Now if we leave the rest of code intact, the variables 'val', 'ref' and 'ptr' will be undeclared in the scope they are used as they are declared and destroyed in the scope of the most nested block they appear in. The obvious solution for this is to move the declarations out of 'if' statement, but in this case we will have two problems. The first one is that the references must be initialized at the time of declaration, and the second is that we cannot keep the 'const' qualifier of the pointer and object as we should assign them different expressions depending on the condition.


int val;
MyClass &ref; // error: reference must be initialized
MyClass *ptr;

if (condition_applies()) {
    val = 5;
    ptr = get_pointer();
} else {
    val = 1;
    ptr = get_another_pointer();
}

How to be then? We can take advantage of one of the most basic operators yet coming from 'C' language, the ternary operator '? :'. Both problems can be resolved with this operator as it always evaluates only one expression and returns it, so the assignable variable does not have to be changed after initialization:


const bool cnd_app = condition_applies();
// note using const wherever possible

const int val = (cnd_app)? 5 : 1;
const MyClass &ref = (cnd_app) ? get_object_of_my_class()
                               : get_another_object_of_my_class();
MyClass *const ptr = (cnd_app) ? get_pointer()
                               : get_another_pointer();


Now all the variables are declared in the same scope as in the first version of code and all the 'const' qualifiers are kept, which was the main task in question. I hope this piece of writing will be useful to at least one person.