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.

2 comments:

  1. Merkur 9C stainless steel safety razor
    Merkur 9C stainless steel safety razor: Merkur 9C razor blade is made in Solingen, Germany, with a stainless steel blade. The william hill blades are link 12bet not 메리트카지노

    ReplyDelete