С точки зрения дизайна, почему в C ++ нет единого базового класса, который обычно используется object
в других языках?
typedef
s. При более общей иерархии классов вы можете просто использовать object
или iterator
.
С точки зрения дизайна, почему в C ++ нет единого базового класса, который обычно используется object
в других языках?
typedef
s. При более общей иерархии классов вы можете просто использовать object
или iterator
.
Ответы:
Окончательное решение можно найти в FAQ Страуструпа . Короче говоря, никакого смыслового значения он не передает. Это будет дорого стоить. Для контейнеров больше подходят шаблоны.
Почему в C ++ нет универсального класса Object?
Он нам не нужен: универсальное программирование в большинстве случаев предоставляет альтернативы, безопасные для статического типа. Другие случаи обрабатываются с использованием множественного наследования.
Нет никакого полезного универсального класса: действительно универсальный не несет в себе собственной семантики.
«Универсальный» класс поощряет небрежное мышление о типах и интерфейсах и приводит к избыточным проверкам во время выполнения.
Использование универсального базового класса требует затрат: объекты должны быть распределены в куче, чтобы быть полиморфными; это подразумевает стоимость памяти и доступа. Объекты кучи, естественно, не поддерживают семантику копирования. Объекты кучи не поддерживают простое поведение с заданной областью (что усложняет управление ресурсами). Универсальный базовый класс поощряет использование dynamic_cast и других проверок времени выполнения.
Objects must be heap-allocated to be polymorphic
- Я не считаю это утверждение в целом правильным. Вы определенно можете создать экземпляр полиморфного класса в стеке и передать его как указатель на один из его базовых классов, что приведет к полиморфному поведению.
Foo
преобразовать ссылку- на-в ссылку- Bar
когда Bar
не наследуется Foo
, детерминированно вызывает исключение. В C ++ попытка преобразовать указатель на Foo в указатель на Bar могла отправить робота назад во времени, чтобы убить Сару Коннор.
Давайте сначала подумаем, зачем вам вообще нужен базовый класс. Я могу придумать несколько разных причин:
Это две веские причины, по которым языки брендов 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 ++ не имеет базового класса, потому что, имея параметрический полиморфизм через шаблоны, он не нужен.
object
( System.Object
), но это не обязательно. Для компилятора int
и System.Int32
являются псевдонимами и могут использоваться как взаимозаменяемые; при необходимости среда выполнения обрабатывает бокс.
std::shared_ptr
который следует использовать.
Доминирующая парадигма для переменных C ++ - передача по значению, а не по ссылке. Принудительное извлечение всего из корня Object
приведет к передаче их по значению с ошибкой ipse facto.
(Потому что принятие объекта по значению в качестве параметра по определению приведет к его разрезанию и удалению его души).
Это нежелательно. C ++ заставляет задуматься о том, нужна ли вам семантика значений или ссылок, предоставляя вам выбор. Это важная вещь для вычислений производительности.
Object
были бы относительно небольшими.
Проблема в том, что в C ++ такой тип ЕСТЬ! Это так void
. :-) Любой указатель можно безопасно неявно привести кvoid *
, включая указатели на базовые типы, классы без виртуальной таблицы и классы с виртуальной таблицей.
Поскольку он должен быть совместим со всеми этими категориями объектов, void
сам по себе не может содержать виртуальных методов. Без виртуальных функций и RTTI невозможно получить полезную информацию о типе void
(он соответствует КАЖДОМУ типу, поэтому может говорить только то, что верно для КАЖДОГО типа), но виртуальные функции и RTTI сделают простые типы очень неэффективными и не позволят C ++ быть язык, подходящий для низкоуровневого программирования с прямым доступом к памяти и т. д.
Итак, есть такой тип. Он просто предоставляет очень минималистичный (фактически пустой) интерфейс из-за низкоуровневой природы языка. :-)
void
.
void
она не будет компилироваться, и вы получите эту ошибку . В Java у вас может быть переменная типа, Object
и она будет работать. В этом разница между void
типом и «настоящим». Возможно, это правда, что void
это базовый тип для всего, но, поскольку он не предоставляет никаких конструкторов, методов или полей, невозможно определить, есть он там или нет. Это утверждение нельзя ни доказать, ни опровергнуть.
void
это тип (и обнаружил несколько умное использование? :
), но он все еще далек от универсального базового класса. Во-первых, это «неполный тип, который невозможно завершить», а во-вторых, void&
тип отсутствует .
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 ++, ответ, вероятно, будет «ни один член комитета не подумал об этом».
ReturnType fallback(ArgTypes...)
будет работать, это будет плохой дизайн. template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...
делает то, что вы хотите, и означает, что параметры шаблона Hook
имеют одинаковые типы
C ++ изначально назывался «C с классами». Это развитие языка C, в отличие от некоторых других более современных вещей, таких как C #. И вы можете рассматривать C ++ не как язык, а как основу языков (да, я вспоминаю книгу Скотта Мейерса «Эффективный C ++»).
Сам по себе C представляет собой смесь языков, языка программирования C и его препроцессора.
C ++ добавляет еще один микс:
подход класса / объекта
шаблоны
STL
Мне лично не нравятся некоторые вещи, которые идут напрямую с C на C ++. Одним из примеров является функция enum. То, как C # позволяет разработчику использовать его, намного лучше: он ограничивает перечисление в своей собственной области видимости, имеет свойство Count и легко повторяется.
Поскольку C ++ хотел быть ретро-совместимым с C, дизайнер был очень снисходительным, позволив языку C полностью войти в C ++ (есть некоторые тонкие различия, но я не помню ничего, что вы могли бы сделать с помощью компилятора C, который вы не мог использовать компилятор C ++).