Расширение Библиотеки Стандарта C++ наследованием?

Я добавлю Подкаст безопасности Криптограммы . В основном, Dan Henage, читающий Bruce Schneier новостная рассылка Криптограмма.

большинство других подкастов я слушаю, чтобы быть упомянутым (TwiT, безопасность Теперь!, Расшатанные Фанаты).

мой 2c

51
задан Community 23 May 2017 в 02:25
поделиться

10 ответов

Хороший хороший вопрос. Мне действительно хотелось бы, чтобы в Стандарте было более ясно, каково предполагаемое использование. Может быть, должен быть документ C ++ Rationale, который находится рядом со стандартом языка. В любом случае, вот подход, который я использую:

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

  • Если он не имеет каких-либо виртуальных методов, то вам не следует использовать это как база. Это исключает std :: vector и т. Д.
  • Если у него есть виртуальные методы, то он является кандидатом для использования в качестве базового класса.
  • Если есть много друг утверждений, плавающих вокруг, затем держитесь подальше, поскольку, вероятно, существует проблема инкапсуляции.
  • Если это шаблон, то присмотритесь к нему, прежде чем наследовать от него, поскольку вместо этого вы, вероятно, можете настроить его со специализациями.
  • Наличие механизма на основе политик (например, , std :: char_traits ) является довольно хорошей подсказкой, что вы не должны использовать его в качестве основы.

К сожалению, я не знаю хорошего всеобъемлющего или черно-белого список. Я обычно исхожу из интуиции.

(b) Я бы применил здесь LSP . Если кто-то вызывает what () для вашего исключения, то его наблюдаемое поведение должно соответствовать поведению std :: exception . Я не думаю, что это действительно вопрос соответствия стандартам, а скорее вопрос правильности. Стандарт не t требуют, чтобы подклассы заменяли базовые классы. На самом деле это просто "лучший метод" .

38
ответ дан 7 November 2019 в 10:13
поделиться

a) потоковая библиотека сделана для наследования :)

17
ответ дан 7 November 2019 в 10:13
поделиться

Стандартная библиотека C ++ не является единым целым. Это результат объединения и принятия нескольких различных библиотек (большая часть стандартной библиотеки C, библиотека iostreams и STL являются тремя основными строительными блоками, и каждый из них был определен независимо)

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

Библиотека IOStreams является гораздо более традиционной ООП и в значительной степени внутренне использует наследование и динамический полиморфизм - и ожидается, что пользователи будут использовать те же механизмы для ее расширения. Пользовательские потоки обычно пишутся производными от самого класса потока, или класс streambuf , который он использует для внутренних целей. У обоих из них есть виртуальные методы, которые можно переопределить в производных классах.

std :: exception - другой пример.

И, как сказал Д.Шоули, я бы применил LSP к ваш второй вопрос. Всегда должна быть законная замена базового класса производным. Если я вызываю exception :: what () , он должен следовать контракту, указанному в классе exception , независимо от того, откуда взялся объект exception , или на самом деле это производный класс, преобразованный вверх. И в этом случае этот контракт является стандартным обещанием вернуть NTBS. Если вы заставили производный класс вести себя иначе, вы нарушите стандарт, потому что объект типа std ::

5
ответ дан 7 November 2019 в 10:13
поделиться

Чтобы ответить на вопрос 2):

Я считаю, что да, они будут связаны описанием интерфейса стандарта ISO. Например, стандарт позволяет переопределить оператор new и оператор delete глобально. Однако стандарт гарантирует, что operator delete не является операцией с нулевыми указателями.

Несоблюдение этого, безусловно, неопределенное поведение (по крайней мере, для Скотта Майерса). Я думаю, мы можем сказать, что то же самое верно по аналогии для других областей стандартной библиотеки.

4
ответ дан 7 November 2019 в 10:13
поделиться

Некоторые вещи в функциональном , например больше <> , меньше <> и mem_fun_t являются производными от unary_operator <> и binary_operator <> . Но, IIRC, это дает вам только некоторые определения типов.

4
ответ дан 7 November 2019 в 10:13
поделиться

Экономное правило: «Любой класс может быть использован в качестве базового класса; ответственность за его безопасное использование в отсутствие виртуальных методов, включая виртуальный деструктор, полностью принадлежит производному автору. " Добавление члена, не являющегося POD, в дочерний элемент std :: exception - такая же ошибка пользователя, как и в производном классе std :: vector. Идея о том, что контейнеры не «предназначены» для использования в качестве базовых классов, является инженерным примером того, что профессора литературы называют ошибкой авторского намерения.

Преобладает принцип IS-A. Не производите D из B, если D не может заменить B во всех отношениях в публичном интерфейсе B, включая операцию удаления указателя B. Если B имеет виртуальные методы, это ограничение менее обременительно; но если у B есть только невиртуальные методы, все еще возможно и законно специализироваться на наследовании.

C ++ мультипарадигматичен. Библиотека шаблонов использует наследование, даже наследование от классов без виртуальных деструкторов, и таким образом демонстрирует на примере, что такие конструкции безопасны и полезны; предназначались ли они - вопрос психологический.

4
ответ дан 7 November 2019 в 10:13
поделиться

по вопросу 2), в соответствии со стандартом C ++, производный класс исключения должен указывать спецификацию no-throw, т.е. throw () , а также возвращать ненулевое значение . Во многих случаях это означает, что производный класс исключений не должен использовать std :: string, так как std :: string сам может вызывать в зависимости от реализации.

1
ответ дан 7 November 2019 в 10:13
поделиться

Думаю, что на второй вопрос ответ положительный. Стандарт говорит, что член what std :: exception должен возвращать значение, отличное от NULL. Не имеет значения, есть ли у меня значение стека, ссылки или указателя на std :: exception. Возврат what () ограничен стандартом.

Конечно, можно вернуть NULL. Но я считаю, что такой класс не соответствует стандартам.

3
ответ дан 7 November 2019 в 10:13
поделиться

Я знаю этот вопрос старый, но я хотел бы добавить сюда свой комментарий.

Уже несколько лет я использую класс CfgValue , который наследуется от std :: string, хотя документация (или какая-то книга или какой-то стандартный документ, у меня сейчас нет исходного кода) говорит, что пользователи не должны наследовать от std :: string. И этот класс содержит комментарий «TODO: удалить наследование из std :: string» с годами.

Класс CfgValue просто добавляет некоторые конструкторы, сеттеры и геттеры для быстрого преобразования между строками и числовыми и логическими значениями, а также преобразование из кодировки utf8 (хранится в std :: string) в кодировку ucs2 (хранится в std :: wstring) и т. Д. .

Я знаю, есть много разных способов сделать это, но это просто чрезвычайно удобно для пользователя, и он отлично работает (что означает, что не ломается. любые концепции stdlib, обработка исключений и т. д.):

class CfgValue : public std::string {
public:
    ...
    CfgValue( const int i ) : std::string() { SetInteger(i); }
    ...
    void SetInteger( int i );
    ...
    int GetInteger() const;
    ...
    operator std::wstring() { return utf8_to_ucs16(*this); }
    operator std::wstring() const { return utf8_to_ucs16(*this); }
    ...
};
1
ответ дан 7 November 2019 в 10:13
поделиться

Что касается вашей части B, от 17.3.1.2 «Требования», пункт 1:

Библиотека может быть расширена программой C ++. Каждое предложение, как применимо, описывает требования, которые такие расширения должны соответствовать. Такие расширения, как правило, являются одним из следующих:

  • шаблон аргументов
  • полученные классы
  • контейнеры, итераторы и / или алгоритмы, соответствующие конвенции по интерфейсу

, в то время как 17.3 является информативным вместо того, чтобы привязать, намерение Комитета Производное поведение класса ясно.

Для других очень похожих точек расширения есть четкие требования:

  • 17.1.15 «Обязательное поведение» охватывает замену (оператор новых и т. Д.) И функции обработчика (завершающие обработчики и т. Д.) И бросают все Соответствующее поведение в UB-Land.
  • 17.4.3.6/1. Соответствуйте их требованиям, стандартные места нет требований к реализации ».

В последнее время не ясно для меня, что список в скобках является исчерпывающим, но учитывая, насколько конкретно каждый упомянутый случай рассматривается в следующем абзаце, это Будьте растягиваться, чтобы сказать, текущий текст был предназначен для покрытия полученных классов. Кроме того, что 17.4.3.6/1 текст не изменился в проекте 2008 года (где он в 17,6,4,8), и я не вижу проблем , касающихся виртуальных методов либо полученных классов.

7
ответ дан 7 November 2019 в 10:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: