Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

stl

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.

like image 397
purga Avatar asked Feb 17 '09 05:02

purga


People also ask

Do inherited classes inherit constructors?

In inheritance, the derived class inherits all the members(fields, methods) of the base class, but derived class cannot inherit the constructor of the base class because constructors are not the members of the class.

Can constructor be inherited in C++?

There can't be any proper inheritance of constructors in C++, because the constructor of a derived class needs to perform additional actions that a base-class constructor does not have to do and does not know about.

Does inherited class need constructor?

Constructor is a block of code that allows you to create an object of class and has same name as class with no explicit return type. If we define Parent class constructor inside Child class it will give compile time error for return type and consider it a method.

How do you create a derived class constructor in C++?

If we inherit a class from another class and create an object of the derived class, it is clear that the default constructor of the derived class will be invoked but before that the default constructor of all of the base classes will be invoke, i.e the order of invocation is that the base class's default constructor ...


3 Answers

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:

  • SGI's STL extension, std::rope. It's in GNU C++, so maybe you can rely on it.
  • Boost has a pretty extensive string algorithm library that lets you use concepts to specify how your string is represented.
  • Also see this if you must "derive" from std::string
like image 149
11 revs, 2 users 94% Avatar answered Sep 20 '22 00:09

11 revs, 2 users 94%


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)

like image 6
sth Avatar answered Sep 24 '22 00:09

sth


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.

like image 4
maxaposteriori Avatar answered Sep 21 '22 00:09

maxaposteriori