Why Should One Not Derive from C++ Std String Class

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

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);
}

What is the way to inherit std::string right?

In C++ any declaration that can be parsed as a function declaration, such as …

    spstring byteaddstr(std::string(argv[4])); //convertchar* to spstring

is parsed as a function declaration.

I.e. not a variable.

One solution in this particular case, is to add extra parentheses:

    spstring byteaddstr(( std::string(argv[4]) )); //convertchar* to spstring

This is known as the most vexing parse in C++, although some people disagree about whether Scott Meyers' original use of that term applies as generally as it's used now.


And by the way, one should generally have a pretty good reason to derive from std::string, because it adds complexity and confusion (you can safely disregard concerns about dynamic allocation, because code that dynamically allocates std::string deserves whatever it gets). So, I suggest that you don't do that.

C++ - Derive class off of std::string class to add extra function?

Deriving from a class simply to add member functions isn't a great idea; especially a non-polymorphic class like std::string. Instead, write non-member functions to do whatever you want with the existing string class.

If you really want to do this nonetheless, you can inherit the constructors:

class MyString : public std::string {
public:
using std::string::string;
};

Now you can initialise your class in any way that you can initialise std::string, including conversion from a character array as in your example. (By the way, "whatever" isn't a std::string, it's a zero-terminated character array, const char[9].)

C++: Is it recommended to create your own String class?

Sounds to me like you have an old book.

The C++ standard library includes the std::string class, and you should be using that instead of inventing your own and fixing bugs that have already been encountered and fixed in a widely used library.

As you are a beginner, yes, you should take a swing at implementing your own as it will teach you quite a bit. Just don't use it in any 'real' project.

Is inheriting from std::basic_string ok?

Well, STL classes have not been designed to be inherited. The major issue with inheriting std::string and others is that they don't have a virtual destructor. That's a big no-no for a base class of public inheritance, as the warning makes clear. It's not nearly as dangerous for private inheritance, though.

There's a bug report for not issuing an error in the case of privately inheriting a base class that has nonvirtual destructor. It hasn't been marked fixed, however my test shows that no warning is issued in g++ 4.9.2 with private inheritance.

Also, a comment in the bug shows how to shoot yourself the foot with (intentionally?) bad code:

class Foo {
public:
~Foo() {}
virtual void f() { }
};

class Bar : private Foo {
public:
Foo* get() { return this; }
};

int main()
{
Bar* b = new Bar;
delete b->get();
}

Privately inheriting a class with nonvirtual destructor is not bad as such if you avoid exposing the inheritance relationship outside the class like in the snippet above. Without going into detail of how you're going to use the inheritance, it's not possible to say if that's the best approach.

Doesn't std::string derive from it?

No, std::string is an alias of std::basic_string<char>.

inherit from std::string or operator overloading

You can provide a user-defined conversion operator to std::string:

class Test {
//...
public:
operator std::string () const {
return /*something*/;
}
};

This will allow a Test object to be implicitly-converted to a std::string.

Why does outputting a class with a conversion operator not work for std::string?

That operator is a free template function. User defined conversions do not get checked when matching against a template function arguments, it instead uses type pattern matching (substitution).

In theory a SFINAE overload using std::is_convertable<> would be able to do what you want, but that technique was not used when operator<< that outputs a std::string to a basic_ostream<char> was defined.

A manual overload to output your class to basic_ostream<...> will fix your problem.

I would do this:

struct String {
std::string s;
operator std::string() const {return s;}
friend std::ostream& operator<<( std::ostream& os, String const& self) {
return os<<self.s;
}
};

which has the added benefit of not creating a wasted copy.

Constructor doesn't work for class inherited from std::string

You need to define some constructors for the different types that you want to be able to convert into your strings. These constructors can basically just hand the parameters through to the underlying std::string.

If you don't manually create them, the compiler creates a default- and a copy-constructor for you:

MyString() : std::string() { }
MyString(const MyString &other) : std::string(other) { }

To allow construction from string literals, you need a constructor that takes a const char*:

MyString(const char* other) : std::string(other) { }

A constructor that takes a const std::string& would also be useful to convert std::strings to your string type. If you want to avoid implicit conversions of normal strings, you should make it explicit:

explicit MyString(const std::string &other) : std::string(other) { }

(Edited because my original version was full of errors and I can't delete the accepted answer)



Related Topics



Leave a reply



Submit