С точки зрения дизайна, почему в C ++ нет единого базового класса, который обычно используется object
в других языках?
c++
language-design
слезица
источник
источник
typedef
s. При более общей иерархии классов вы можете просто использоватьobject
илиiterator
.Ответы:
Окончательное решение можно найти в FAQ Страуструпа . Короче говоря, никакого смыслового значения он не передает. Это будет дорого стоить. Для контейнеров больше подходят шаблоны.
источник
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) { ... } };
со специализированным экземпляром
Но, увы, даже несмотря на то, что класс 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 ++).
источник