Есть ли какой-либо реальный риск для получения из контейнеров STL C ++?

Утверждение о том, что это ошибка при использовании стандартного контейнера C ++ в качестве базового classа, меня удивляет.

Если это не злоупотребление языком, чтобы объявить …

// Example A typedef std::vector Rates; typedef std::vector Charges; 

… то что, собственно, есть опасность в объявлении …

 // Example B class Rates : public std::vector { // ... } ; class Charges: public std::vector { // ... } ; 

К положительным преимуществам B относятся:

  • Позволяет перегружать функции, потому что f (Тарифы) и f (Сборы &) – различные подписи
  • Позволяет специализировать другие шаблоны, поскольку X и X – это разные типы
  • Форвардная декларация тривиальна
  • Вероятно, отладчик говорит вам, является ли объект тарифами или тарифами
  • Если со временем тарифы и сборы будут развиваться личностями – Singleton for Rates, формат вывода для платежей – есть очевидная возможность для этой функциональности, которая будет реализована.

Положительные преимущества для A include:

  • Не нужно предоставлять тривиальные реализации конструкторов и т. Д.
  • Пятнадцатилетний предварительный стандартный компилятор – это единственное, что скомпилирует ваше наследие, не задушит
  • Поскольку специализации невозможны, шаблон X и шаблон X будут использовать один и тот же код, поэтому бессмысленного раздувания.

Оба подхода превосходят использование необработанного контейнера, потому что, если реализация меняется с вектора на vector , есть только одно место для изменения с B и, возможно, только одно место для изменения с A (это может быть больше, потому что кто-то может поставить одинаковые инструкции typedef в нескольких местах).

Моя цель заключается в том, что это конкретный, ответный вопрос, а не обсуждение лучшей или худшей практики. Покажите худшее, что может произойти в результате получения стандартного контейнера, которое было бы предотвращено с помощью typedef.

Редактировать:

Несомненно, добавление деструктора в class «Тарифы» или «Сцены classа» было бы риском, потому что std :: vector не объявляет его деструктор виртуальным. В примере нет деструктора, и нет необходимости в нем. Уничтожение объекта тарифов или сборов вызовет деструктор базового classа. Здесь нет необходимости в polymorphismе. Задача состоит в том, чтобы показать что-то плохое из-за использования деривации вместо typedef.

Редактировать:

Рассмотрим этот вариант использования:

 #include  #include  void kill_it(std::vector *victim) { // user code, knows nothing of Rates or Charges // invokes non-virtual ~std::vector(), then frees the // memory allocated at address victim delete victim ; } typedef std::vector Rates; class Charges: public std::vector { }; int main(int, char **) { std::vector *p1, *p2; p1 = new Rates; p2 = new Charges; // ??? kill_it(p2); kill_it(p1); return 0; } 

Есть ли какая-либо возможная ошибка, которую даже произвольно несчастный пользователь может ввести в ??? раздел, который приведет к проблеме с тарифами (производным classом), но не с тарифами (typedef)?

В реализации Microsoft вектор сам реализуется через наследование. вектор является общедоступным из _Vector_Val Следует ли содержать ограничение?

8 Solutions collect form web for “Есть ли какой-либо реальный риск для получения из контейнеров STL C ++?”

Стандартные контейнеры не имеют виртуальных деструкторов, поэтому вы не можете обрабатывать их полиморфно. Если вы этого не сделаете, и каждый, кто использует ваш код, нет, это не «неправильно», как таковое. Тем не менее, вам лучше использовать композицию в любом случае, для ясности.

Потому что вам нужен виртуальный деструктор, а в std-контейнерах его нет. Контейнеры std не предназначены для использования в качестве базового classа.

Для получения дополнительной информации прочитайте статью «Почему мы не должны наследовать class из classов STL?»

директива

Базовый class должен иметь:

  • публичный виртуальный деструктор
  • или защищенный не виртуальный деструктор

Один сильный контраргумент, на мой взгляд, заключается в том, что вы вводите интерфейс и реализацию на свои типы. Что произойдет, когда вы узнаете, что страtagsя распределения памяти в векторе не соответствует вашим потребностям? Вы получите из std:deque ? Как насчет тех 128K строк кода, которые уже используют ваш class? Все ли нужно перекомпилировать все? Будет ли он даже компилироваться?

Проблема не философская, это проблема внедрения. Деструкторы стандартных контейнеров не являются виртуальными, а это значит, что для их правильного дескриптора не существует возможности использовать polymorphism во время выполнения.

Я нашел на практике, что на самом деле не так уж больно создавать собственные classы списка, используя только те методы, которые определены моим кодом (и частный член classа «parent»). Фактически, это часто приводит к более продуманным classам.

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

Тарифы и сборы, если на то пошло, ЯВЛЯЮТСЯ ТОЛЬКО КАК вектор двойников в вашем примере выше. По вашему собственному утверждению «… со временем, тарифы и сборы развивают личностей …», то есть утверждение, что ставки ВСЕ ЕЩЕ ОСТАЮТСЯ КАК вектор двойников на данный момент? Например, вектор двойников не является синглоном, поэтому, если я использую ваши ставки для объявления моего вектора удвоений для виджетов, я могу понести некоторую головную боль из вашего кода. Что еще о тарифах и сборах могут быть изменены? Являются ли какие-либо изменения базового classа безопасно изолированными от клиентов вашего проекта, если они будут меняться фундаментальным образом?

Точка – это class, который является элементом многих из C ++, чтобы выразить намерения дизайна. Говорить о том, что вы имеете в виду и что означает то, что вы говорите, является причиной против использования наследования таким образом.

… Или просто выложил более кратко до моего ответа: Замена.

Кроме того, в большинстве случаев вам следует предпочесть состав или агрегацию по наследованию, если это возможно.

Одно слово: подстановка

Есть ли какая-либо возможная ошибка, которую даже произвольно несчастный пользователь может ввести в ??? раздел, который приведет к проблеме с тарифами (производным classом), но не с тарифами (typedef)?

Во-первых, замечательный момент Манкарсе:

Комментарий в kill_it неверен. Если динамический тип жертвы не является std::vector , то delete просто вызывает неопределенное поведение. Вызов kill_it(p2) заставляет это произойти, и поэтому ничего не нужно добавлять в //??? для этого, чтобы иметь неопределенное поведение. – Mankarse Сен 3 ’11 в 10:53

Во-вторых, скажем, что они называются f(*p1); где f специализирован для std::vector : эта vector специализация не будет найдена – вы можете в конечном итоге совместить специализацию шаблона по-другому – обычно выполняемый (медленный или менее эффективный) общий код или получение ошибки компоновщика if неспециализированная версия на самом деле не определена. Не часто значительная проблема.

Лично я считаю, что уничтожение через указатель основывается на пересечении линии – это может быть только «гипотетическая» проблема (насколько это возможно) с учетом вашего текущего компилятора, флагов компилятора, программы, версии ОС и т. Д. – но это может сломаться в любое время без «хорошей» причины.

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

Тем не менее, несколько замечаний по вашей оценке:

  • «предоставление тривиальных реализаций конструкторов» – это хлопот, но один совет для C ++ 03: template Classname(const A& a) : Base(a) { } template Classname(const A& a, const B& b) : Base(a, b) { } ... иногда может быть проще, чем перечисление всех перегрузок, но не обрабатывает const параметры, значения по умолчанию, явные-vs-неявные конструкторы , а также масштабность до огромного количества аргументов. C ++ 11 обеспечивает лучшее общее решение.

Несомненно, добавление деструктора в class Rates или « class Charges было бы риском, потому что std::vector не объявляет его деструктор виртуальным. В примере нет деструктора, и нет необходимости в нем. Уничтожение объекта тарифов или сборов вызовет деструктор базового classа. Здесь нет необходимости в polymorphismе.

  • Нет риска, связанного с деструктором производного classа, если объект не удаляется полиморфно; если существует неопределенное поведение независимо от того, имеет ли ваш производный class определенный пользователем деструктор. Тем не менее, вы переходите от «вероятно-ok-for-a-cowboy» к «почти-конечно-не-ок», когда добавляете элементы данных или другие базы с деструкторами, выполняющими очистку (освобождение памяти, разблокирование мьютексов, файл ручка закрытия и т.д.)

  • Высказывание «вызовет деструктор базового classа» заставляет его звучать так, как будто это делается напрямую, без участия неявно определяемого деструктора производного classа или выполнения вызова – все детали оптимизации и не указаны стандартом.

  • Полученный доступ к шаблону classа к элементам-членам базового classа
  • Как виртуальное наследование разрешает двусмысленность «бриллиантов» (множественное наследование)?
  • Переопределение частных методов в Java
  • Как расширить class без использования супер в ES6?
  • Почему не следует выводить из c ++ std string class?
  • Что такое нарезка объектов?
  • Приведение C ++ в производный class
  • Во время выполнения найдите все classы в приложении Java, которые расширяют базовый class
  • Разница между новыми и отменой
  • Вызов метода подclassа из суперclassа
  • Как я могу инициализировать переменные-члены базового classа в конструкторе производного classа?
  • Interesting Posts

    Проводник Windows сортирует файлы и папки отдельно

    Как отключить отображение в Windows 8 без блокировки или заставить компьютер переспать?

    Java: добавьте несколько строк в MySQL с помощью PreparedStatement

    Изменение поведения Windows 8 Win + Space changer

    Google не открывается в браузерах

    Экспортировать таблицу PostgreSQL в файл CSV с заголовками

    Каковы все функции-члены, созданные компилятором для classа? Это происходит постоянно?

    В чем разница между каталогом и папкой?

    Создать образ ISO текущей системы Windows 7?

    Как вы можете вырезать не-ASCII-символы из строки? (в C #)

    Как связать несколько экземпляров Firefox с моей панелью задач?

    Перехват ссылок из браузера, чтобы открыть приложение для Android

    Как я могу печатать на одной строке?

    Objective-C – CABasicAnimation, применяющая изменения после анимации?

    Я ищу программное обеспечение для мониторинга последовательного порта

    Давайте будем гением компьютера.