Почему в С++ нет базового класса?

Быстрый вопрос: с точки зрения дизайна, почему это, на С++, нет базового класса материнства, что обычно object на других языках?

Ответ 1

Окончательное решение найдено в Часто задаваемые вопросы по Stroustrup. Короче говоря, это не передает никакого смыслового смысла. Это будет стоить. Шаблоны более полезны для контейнеров.

Почему у С++ нет универсального класса Object?

  • Нам это не нужно: универсальное программирование в большинстве случаев обеспечивает статические типы безопасных альтернатив. Другие случаи обрабатываются с использованием множественного наследования.

  • Нет полезного универсального класса: поистине универсальное не имеет собственной семантики.

  • Универсальный класс поощряет небрежное мышление о типах и интерфейсах и приводит к избыточной проверке времени выполнения.

  • Использование универсального базового класса подразумевает стоимость: объекты должны быть выделены в виде кучи, чтобы быть полиморфными; что подразумевает стоимость памяти и доступа. Объекты кучи, естественно, не поддерживают семантику копирования. Объекты кучи не поддерживают простое поведение (что усложняет управление ресурсами). Универсальный базовый класс поощряет использование dynamic_cast и других проверок времени выполнения.

Ответ 2

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

  • Поддерживать общие операции или коллекции, которые будут работать с объектами любого типа.
  • Чтобы включить различные процедуры, общие для всех объектов (например, управление памятью).
  • Все - объект (нет примитивов!). Некоторые языки (например, Objective-C) не имеют этого, что делает вещи довольно грязными.

Это две хорошие причины, по которым языки брендов Smalltalk, Ruby и Objective-C имеют базовые классы (технически, Objective-C на самом деле не имеет базового класса, но для всех целей и целей он делает).

Для # 1 необходимость в базовом классе, который объединяет все объекты под одним интерфейсом, устраняется включением шаблонов в С++. Например:

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

не требуется, когда вы можете поддерживать целостность типа полностью путем параметрического полиморфизма!

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

Для # 2, тогда как в Objective-C процедуры управления памятью являются частью реализации класса и наследуются от базового класса, управление памятью на С++ выполняется с использованием композиции, а не наследования. Например, вы можете определить оболочку интеллектуального указателя, которая будет выполнять подсчет ссылок на объекты любого типа:

template <class T>
struct refcounted
{
  refcounted(T* object) : _object(object), _count(0) {}

  T* operator->() { return _object; }
  operator T*() { return _object; }

  void retain() { ++_count; }

  void release()
  {
    if (--_count == 0) { delete _object; }
  }

  private:
    T* _object;
    int _count;
};

Затем вместо вызова методов на самом объекте вы будете вызывать методы в своей оболочке. Это не только позволяет более общее программирование: оно также позволяет разделить проблемы (так как в идеале ваш объект должен больше заботиться о том, что он должен делать, чем о том, как его память должна управляться в разных ситуациях).

Наконец, на языке, который имеет как примитивы, так и фактические объекты, такие как С++, преимущества наличия базового класса (согласованного интерфейса для каждого) теряются, так как тогда у вас есть определенные значения которые не могут соответствовать этому интерфейсу. Чтобы использовать примитивы в подобной ситуации, вам нужно поднять их на объекты (если ваш компилятор не сделает это автоматически). Это создает много осложнений.

Итак, короткий ответ на ваш вопрос: С++ не имеет базового класса, потому что, имея параметрический полиморфизм через шаблоны, ему не нужно.

Ответ 3

Доминирующая парадигма для переменных С++ является pass-by-value, а не pass-by-ref. Принуждение всего, что должно быть получено от корня Object, приведет к передаче им по значению ошибки ipse facto.

(Поскольку принятие объекта по значению как параметра, по определению разрезает его и удаляет его душу).

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

Ответ 4

Проблема в том, что в С++ существует такой тип! Это void.:-) Любой указатель может быть безоговорочно приложен к void *, включая указатели на базовые типы, классы без виртуальной таблицы и классы с виртуальной таблицей.

Так как он должен быть совместим со всеми этими категориями объектов, void сам не может содержать виртуальные методы. Без виртуальных функций и RTTI никакая полезная информация о типе не может быть получена из void (она соответствует КАЖДОМУ типу, поэтому может указывать только то, что истинно для КАЖДОГО типа), но виртуальные функции и RTTI сделают простые типы очень неэффективными и прекратят С++ - язык, подходящий для низкоуровневого программирования с прямым доступом к памяти и т.д.

Итак, есть такой тип. Он просто обеспечивает минималистичный (фактически, пустой) интерфейс из-за низкого уровня языка.: -)

Ответ 5

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

Возьмем, например, шаблон

template <class T> class Hook;
template <class ReturnType, class ... ArgTypes>
class Hook<ReturnType (ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

который может быть создан как

Hook<decltype(some_function)> ...;

Теперь предположим, что мы хотим того же для конкретной функции. Как

template <auto fallback> class Hook;
template <auto fallback, class ReturnType, class ... ArgTypes>
class Hook<ReturnType fallback(ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

со специализированным экземпляром

Hook<some_function> ...

Но, увы, хотя класс T может стоять за любым типом (классом или нет) до специализации, нет эквивалента auto fallback (я использую этот синтаксис как наиболее очевидный общий нетипный в этом контексте) который может стоять за любым аргументом шаблона без типа перед специализацией.

Таким образом, в общем случае этот шаблон не переносит от аргументов шаблона шаблона аргументы шаблона не-типа.

Как и с большим количеством углов на языке С++, ответ, скорее всего, "ни один член комитета не думал об этом".

Ответ 6

С++ изначально назывался "C с классами". Это прогрессия языка C, в отличие от некоторых других более современных вещей, таких как С#. И вы не можете видеть С++ как язык, а как основу языков (да, я помню книгу Скотта Мейерса "Эффективный С++" ).

C сам по себе представляет собой сочетание языков, языка программирования C и его препроцессора.

С++ добавляет еще один микс:

  • подход класса/объектов

  • Шаблоны

  • STL

Мне лично не нравится какой-то материал, который приходит непосредственно с C на С++. Одним из примеров является функция перечисления. Способ, которым С# позволяет разработчику использовать его, намного лучше: он ограничивает перечисление в своей области, имеет свойство Count, и это легко итеративно.

Поскольку С++ хотел быть совместимым с C, разработчик был очень разрешительным, чтобы позволить C-языку вводить все его в С++ (есть некоторые тонкие различия, но я не помню ничего, что вы могли бы сделать, используя C компилятор, который вы не могли бы использовать с помощью компилятора С++).