Why should one not derive from c++ std string class?

c++, inheritance, stl, string

Solution

I think this statement reflects the confusion here (emphasis mine):

I do not understand what specifically is required in a class to be eligible for being a base clas (not a polymorphic one)?

In idiomatic C++, there are two uses for deriving from a class:

- private inheritance, used for mixins and aspect oriented programming using templates.

- public inheritance, used for polymorphic situations only. EDIT: Okay, I guess this could be used in a few mixin scenarios too -- such as `boost::iterator_facade` -- which show up when the CRTP is in use.

There is absolutely no reason to publicly derive a class in C++ if you're not trying to do something polymorphic. The language comes with free functions as a standard feature of the language, and free functions are what you should be using here.

Think of it this way -- do you really want to force clients of your code to convert to using some proprietary string class simply because you want to tack on a few methods? Because unlike in Java or C# (or most similar object oriented languages), when you derive a class in C++ most users of the base class need to know about that kind of a change. In Java/C#, classes are usually accessed through references, which are similar to C++'s pointers. Therefore, there's a level of indirection involved which decouples the clients of your class, allowing you to substitute a derived class without other clients knowing.

However, in C++, classes are value types -- unlike in most other OO languages. The easiest way to see this is what's known as the slicing problem. Basically, consider:

int StringToNumber(std::string copyMeByValue)
{
    std::istringstream converter(copyMeByValue);
    int result;
    if (converter >> result)
    {
        return result;
    }
    throw std::logic_error("That is not a number.");
}

If you pass your own string to this method, the copy constructor for `std::string` will be called to make a copy, not the copy constructor for your derived object -- no matter what child class of `std::string` is passed. This can lead to inconsistency between your methods and anything attached to the string. The function `StringToNumber` cannot simply take whatever your derived object is and copy that, simply because your derived object probably has a different size than a `std::string` -- but this function was compiled to reserve only the space for a `std::string` in automatic storage. In Java and C# this is not a problem because the only thing like automatic storage involved are reference types, and the references are always the same size. Not so in C++.

Long story short -- don't use inheritance to tack on methods in C++. That's not idiomatic and results in problems with the language. Use non-friend, non-member functions where possible, followed by composition. Don't use inheritance unless you're template metaprogramming or want polymorphic behavior. For more information, see Scott Meyers' Effective C++ Item 23: Prefer non-member non-friend functions to member functions.

EDIT: Here's a more complete example showing the slicing problem. You can see it's output on codepad.org

#include <ostream>
#include <iomanip>

struct Base
{
    int aMemberForASize;
    Base() { std::cout << "Constructing a base." << std::endl; }
    Base(const Base&) { std::cout << "Copying a base." << std::endl; }
    ~Base() { std::cout << "Destroying a base." << std::endl; }
};

struct Derived : public Base
{
    int aMemberThatMakesMeBiggerThanBase;
    Derived() { std::cout << "Constructing a derived." << std::endl; }
    Derived(const Derived&) : Base() { std::cout << "Copying a derived." << std::endl; }
    ~Derived() { std::cout << "Destroying a derived." << std::endl; }
};

int SomeThirdPartyMethod(Base /* SomeBase */)
{
    return 42;
}

int main()
{
    Derived derivedObject;
    {
        //Scope to show the copy behavior of copying a derived.
        Derived aCopy(derivedObject);
    }
    SomeThirdPartyMethod(derivedObject);
}

Problem

I wanted to ask about a specific point made in Effective C++. It says: A destructor should be made virtual if a class needs to act like a polymorphic class. It further adds that since `std::string` does not have a virtual destructor, one should never derive from it. Also `std::string` is not even designed to be a base class, forget polymorphic base class. I do not understand what specifically is required in a class to be eligible for being a base class (not a polymorphic one)? Is the only reason that I should not derive from `std::string` class is it does not have a virtual destructor? For reusability purpose a base class can be defined and multiple derived class can inherit from it. So what makes `std::string` not even eligible as a base class? Also, if there is a base class purely defined for reusability purpose and there are many derived types, is there any way to prevent client from doing `Base* p = new Derived()` because the classes are not meant to be used polymorphically?

Original source

Related problems