2009-02-17 2 views
5

при выполненииКонструктор не работает для класса, унаследованного от станда :: струнной

#include <string> 

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

Но ниже использование:

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

ни один из них работает.

Кажется, что что-то связано с объявлением/переопределением конструкторов/операторов, но может ли кто-нибудь помочь указать, где я могу найти эти ресурсы?

Спасибо.

+0

Когда вы наследуете, вы скрываете нестандартные конструкторы базового класса. Если вы хотите вызвать эти конструкторы, вам необходимо определить нестандартные конструкторы в вашем подклассе, которые их вызывают. – ChrisW

+1

Вы не должны наследовать от std :: string. Вы можете сказать, что это связано с тем, что у него нет виртуального деструктора. Вы открываете себя перед множеством потенциальных проблем. Сделайте std :: string членом. –

ответ

6

Вам нужно определить некоторые конструкторов для различных типов, которые вы хотите, чтобы иметь возможность конвертировать в ваши строки. Эти конструкторы могут в основном просто передавать параметры до базового std::string.

Если вы не вручную создавать их, компилятор создает default- и копию-конструктор для вас:

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

Чтобы разрешить строительство из строковых литералов, вам нужен конструктор, который принимает const char*:

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

конструктор, который принимает const std::string& также будет полезным для преобразования std::string с для вашего типа строки. Если вы хотите, чтобы избежать неявного преобразования нормальных строк, вы должны сделать это explicit:

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

(под ред, потому что моя оригинальная версия была полна ошибок и я не могу удалить принятый ответ)

+0

Спасибо за предоставление конструктора копирования. – purga

+0

Я не вижу разницы между «MyString s» («Счастливый день») и «MyString s (« abc »)». Конечно, вы ошибаетесь. –

+0

Имейте в виду, что это позволяет компилятору подставлять MyString для std :: string при разрешении перегрузок и т. Д.Это может быть то, что вы хотите, или привести к ужасной путанице. Я видел оба случая. –

1

Вы определяете ctor MyString, который не принимает аргументов. Если переопределяет другие ctors, так что ctor не принимает строковый аргумент вообще.

Вы должны CTOR одного аргумента типа const char *, что-то вроде

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

(Не доверяйте синтаксис, посмотреть его, я не пишу C++ каждый день больше.)

Проверьте раздел в C++ FAQ Lite on ctors.

(К сожалению. Const символ *, а не строка. Об этом сообщил вам, что я не пишу C++ каждый день.)

3

Проблема заключается в том, что вам нужно перегрузить конструктор, который принимает константный символ * и вызовите базовый класс конструктор следующим образом:

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

Все три ваших теста должны работать тогда.

3

std :: string не предназначен для унаследованных. У него нет виртуальных методов, поэтому вы не можете переопределить какие-либо его методы.

Вы должны изучить композицию. Или просто создавать служебные функции, которые работают на std :: strings

34

std::string не предназначен для наследования. Он не имеет любых виртуальных функций (даже не деструктор!), Поэтому вы не можете переопределить что-либо. Он также не имеет защищенного интерфейса, поэтому вы ничего не получаете от подкласса, который вы не могли бы получить, создав некоторые автономные служебные функции, которые принимают std::string.

Имейте в виду, что большинство реализаций STL предполагают, что вы используете std::string с семантикой семантики, а не с помощью семантики ссылок, и это делает случай более сложным добавление унаследованных полей или переопределяющих функций.

Если у вас действительно хотите что-то вроде std::string с дополнительными функциями, вы можете рассмотреть возможность использования композиции вместо наследования, но это тоже не очень удобно. Вам не нужно беспокоиться о том, что деструктор не получил правильное название, но вам нужно обернуть множество методов от std::string, которые вам нужны, что очень утомительно. Кроме того, ваши служебные функции будут работать только с MyString, когда большинство кода ожидают std::string, поэтому он не очень многоразовый.

Вам лучше сделать некоторые служебные функции, которые принимают std::string.Или, если std::string не предоставляет то, что вам нужно, вы должны пойти с некоторой другой реализацией строки, которая соответствует вашим потребностям. Вот некоторые возможности, которые приходят на ум:

+0

Конечно, вы можете унаследовать его. Вам просто нужно убедиться, что оно разрешимо во время компиляции. –

+0

Я не сказал, что вы ** не могли унаследовать его. Просто потому, что вы мало зарабатываете. – tgamblin

+0

@Charlie: вы * можете *, он просто ничего не покупает, так как вы не можете перегружать что-либо. Вам лучше выполнять функции утилиты, которые используют методы std :: string. –

4

Дно что вы не должны этого делать. Деструктор на std::string не является виртуальным. Это означает, что если вы сделаете следующее:

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! 
} 

Единственный способ это может быть безопасным, если вы не добавлять пользователей в строку. Вы можете подумать, что вам сейчас не нужно, но кто-то, кто не знает об этой проблеме, может прийти позже (или вы, когда вы забыли этот совет, возможно) и добавите его, а затем, эй, престо, у вас есть трудно отследить утечку памяти.