Почему в C ++ нет базового класса? - PullRequest
83 голосов
/ 18 апреля 2011

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

Ответы [ 6 ]

115 голосов
/ 18 апреля 2011

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

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

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

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

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

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

43 голосов
/ 18 апреля 2011

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

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

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

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

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

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

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

Для # 2, тогда как в Objective-C процедуры управления памятью являются частью реализации класса и наследуются от базового класса, управление памятью в 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;
};

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

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

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

15 голосов
/ 18 апреля 2011

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

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

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

6 голосов
/ 05 февраля 2013

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

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

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

0 голосов
/ 25 декабря 2016

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

Взять, к примеру, шаблон

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 (я использую этот синтаксис как наиболее очевидный универсальный не тип в этом контексте) это может заменить любой не типовой аргумент шаблона перед специализацией.

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

Как и во многих других углах языка C ++, ответ, скорее всего, «никто из членов комитета не думал об этом».

0 голосов
/ 18 апреля 2011

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

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

C ++ добавляет еще одну смесь:

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

  • шаблоны

  • STL

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

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

...