Разрешено ли наследовать от класса в пространстве имен std (а именно std :: wstring)? - PullRequest
7 голосов
/ 07 декабря 2009

В классе std :: wstring отсутствуют некоторые операции для "обычных" строк c (и литералов).

Я хотел бы добавить эти недостающие операции в свой собственный класс:

#include <string>

class CustomWString : public std::wstring {

        public:

            CustomWString(const char*);
            const char* c_str(void);
};

Приведенный выше код прекрасно компилируется на Ubuntu Karmic с g ++ v4.4.1.

Но мне интересно, есть ли аргументы против этого?

РЕДАКТИРОВАТЬ: несколько примеров, чтобы прояснить, что я имею в виду под "отсутствующими операциями":

std::wstring foo("hello"); // requires the L() macro or something like that
std::string bar("hello");
std::wstring goose(bar.c_str());

foo="hello";  // requires the L() macro
foo=bar.c_str();
foo=bar;

РЕДАКТИРОВАТЬ: Я хотел бы, чтобы это как-то "централизовано". Это потому, что у меня есть проект для переноса из M $ -Windows с тысячами этих неудачных операций.

Хорошая вещь: есть одно центральное место, где определен тип используемой строки, например ::

#ifdef WINDOWS_OS
    typedef std::wstring AppStringClass;
#else
    typedef std::string AppStringClass;
#endif

Ответы [ 8 ]

15 голосов
/ 07 декабря 2009

Это законно, но многие считают это нежелательным. Проблема в том, что стандартные классы шаблонов библиотек не имеют виртуальных деструкторов. Так что если вы когда-нибудь напишите код, подобный этому:

std::wstring * p = new CustomWString ;
...
delete p;

у вас неопределенное поведение.

10 голосов
/ 07 декабря 2009

Компилятор определенно позволяет вам, так как нет способа запечатать класс с помощью открытого конструктора в C ++.

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

Однако я бы посчитал это серьезным предупреждающим знаком, а не полным запретом. Это «вы действительно уверены, что делаете, знаете ли вы все последствия и имеете веские аргументы против всех альтернатив»?

В вашем случае: Предоставление дополнительных конструкторов - это хорошо (поскольку открытый интерфейс задокументирован, и вы можете его воспроизвести), и до тех пор, пока вы не вводите новых членов данных или VMT, нарезку или Виртуальный деструктор не проблема. (Имейте в виду, что стандарт C ++ требует неопределенного поведения при удалении через указатель базового класса без виртуального DTor)

Однако Я не уверен, как вы хотите реализовать char const * c_str(), не предоставляя также хранилище для строки. И как только вы вводите новых членов данных, вы вступаете в поле наземных мин.

3 голосов
/ 07 декабря 2009

Можно и законно. Но это плохая идея. Классы не предназначены для наследования, и их расширение лучше всего выполнять без наследования. Если вы не будете осторожны, когда используете классы, вы получите неопределенное поведение. (У классов нет виртуальных деструкторов)

Более того, кажется, что добавлять эти конкретные функции - плохая идея, поскольку они зависят от локали и кодировки. Вы даете классу более чем одну ответственность. Функциональность преобразования между char и wchar_t находится в другом месте.

3 голосов
/ 07 декабря 2009

Традиционный аргумент против наследования от объектов STL состоит в том, что у них нет виртуального деструктора. Итак, возьмем вектор для примера. Если бы я унаследовал от него:

  class CMyVect : public std::vector<int>
  {
      ...
     int* someMemory;
  public:
     CMyVect()
     {
        someMemory = new int();
     }

     ~CMyVect()
     {
        delete someMemory;
     }
  };

Невиртуальный деструктор в базовом классе означает, что если пользователь этого объекта указывает на вектор-вектор-объект на CMyVect, а затем удаляет этот указатель, ваш деструктор не будет вызван, что приведет к утечке памяти. То есть:

 int main()
 {
     std::vector<int>* vecPtr = new CMyVect();
     delete vecPtr; // invokes vector constructor only, not yours
 }

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

3 голосов
/ 07 декабря 2009

Я думаю, вы знаете, что basic_string не определяет виртуальный деструктор.НЕПРАВИЛЬНО использовать wstring полиморфным способом *1003*.В противном случае вы можете использовать свой пользовательский класс как обычно.

Таким образом, следующее опасно:

vector<wstring*> mystrings;
mystrings.push_back(new CustomWString);
...
delete mystrings[0]; // No virtual destructor

Использование вашего пользовательского класса - это нормально, например,

vector<CustomWString> mystrings; // OK
mystrings.push_back("...");
2 голосов
/ 07 декабря 2009

Шаблон класса std::basic_string (из которых std::wstring является typedef с конкретными аргументами шаблона) не имеет виртуальный деструктор, и поэтому публичное наследование от него плохое идея .

1 голос
/ 07 декабря 2009

Вы можете сделать это. Но я не вижу смысла, так как std :: basic_string (и его конкретные версии std :: string и std :: wstring) не определять виртуальные методы и иметь не виртуальный деструктор. Таким образом, нет никакого способа, вы можете использовать свой подкласс правильно, например, вместо станд :: wstring.

0 голосов
/ 10 декабря 2009

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

Однако следующим вопросом будет вопрос «почему». Почему вы хотите наследовать от std::wstring?

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

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

Другими словами, нет никакого способа дать конкретный ответ на ваш вопрос, не принимая во внимание общую картину. В любом случае, остерегайтесь «механических» ответов, например, «поскольку std::wstring не имеет виртуального деструктора, вы не можете наследовать его, поскольку он сломается <переходите к иллюстрации с кодом> ». Такие рассуждения являются полностью поддельными, даже в рамках подхода ООП.

...