tags:

views:

975

answers:

6

when doing

#include <string>

class MyString : public std::string 
{
 public:    
   MyString() {}
};

But the usage below:

MyString s = "Happy day";
MyString s("Happy Day");
MyString s = (MyString)"Happy day";

neither of them works.

It seems that there's something to do with constructors/operators declaration/overridding, but can anyone help point out where may I find these resources?

Thanks.

+1  A: 

You're defining a ctor MyString that takes no arguments. If overrides the other ctors, so there's no ctor taking a string argument at all.

You need to ctor of one argument of type const char *, something like

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

(Don't trust the syntax, look it up; I don't write C++ every day any longer.)

Check the section in the C++ FAQ Lite on ctors.

(Oops. Const char *, not string. Told you I didn't write C++ every day.)

Charlie Martin
+3  A: 

The problem is that you need to overload the constructor that takes const char* and call the base class constructor as follows:

class MyString : public std::string {
   public:    
      MyString() {}
      MyString( const char* c ) : std::string( c )  {}
};

All three of your tests should work then.

MahlerFive
+3  A: 

std::string isn't intended to be inherited from. It doesn't have any virtual methods so you can't override any of its methods.

You should look into composition. Or simply creating utility functions which operate on std::strings

Evan Teran
+30  A: 

std::string isn't designed for inheritance. It doesn't have any virtual functions (not even the destructor!), so you can't override anything. It also doesn't have a protected interface, so you gain nothing from subclassing that you couldn't get by making some standalone utility functions that take std::string.

Keep in mind that most STL implementations expect you to use std::string with copy semantics, not reference semantics, and this makes the case for adding inherited fields or overriding functions even weaker.

If you really want something like std::string with extra functions, you could consider using composition instead of inheritance, but this isn't great either. You don't have to worry about the std::string destructor not getting called properly, but you do end up having to wrap a lot of methods from std::string that you need, which is tedious. Also, your utility functions will only work with MyString when most code is going to expect std::string, so it isn't very reusable.

You're better off making some utility functions that take std::string. Or, if std::string isn't providing what you need, you should go with some other string implementation that suits your needs. Here are some possibilities that come to mind:

tgamblin
Of course you can inherit from it. You just have to make sure its resolvable at compile time.
Charlie Martin
I didn't say you **couldn't** inherit from it. Just that you gain little by doing so.
tgamblin
@Charlie: you *can*, it just doesn't buy you anything since you can't overload anything. You are better off making utility functions which make use of std::string's methods.
Evan Teran
Thanks for pointintout the std::rope extension. It's useful.
Inheriting from std::string is a BAD idea.
Martin York
Hm. I didn't realize that editing 5 times makes things community wiki. Oh well. Feel free to polish this everyone!
tgamblin
+4  A: 

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)

sth
Thanks for providing the copy constructor.
I don't see the difference between 'MyString s("Happy Day")' and 'MyString s("abc")'. Surely you are mistaken.
Dave Van den Eynde
Bear in mind that this allows the compiler to substitute MyString for std::string when resolving overloads, etc. This may be what you want, or lead to horrible confusion. I've seen both cases.
David Thornley
+3  A: 

The bottom line is that you shouldn't do this. The destructor on std::string isn't virtual. This means that if you do the following:

std::vector<std::string*> s_vector;
s_vector.push_back(new MyString("Hello"));
s_vector.push_back(new std::string("World"));

const std::vector<std::string*>::iterator s_vector_end = s_vector.end();
std::vector<std::string*>::iterator s = s_vector.begin();
for (; s != s_vector_end; ++s)
{
    delete *s; // Error, MyString's destructor will
               // not be called, but std::string's!
}

The only way this might be safe is if you don't add members to your string. You might think that you don't need any now, but someone who isn't aware of these issue may come along later (or you, when you've forgotten this advice perhaps) and add one, and then hey presto, you have a difficult to track down memory leak.

Andy