Быстрый вопрос: с точки зрения дизайна, почему это, на С++, нет базового класса материнства, что обычно 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 компилятор, который вы не могли бы использовать с помощью компилятора С++).