Швидке запитання: з точки зору дизайну, чому в C ++ немає базового класу, який зазвичай є object
іншими мовами?
typedef
s. Маючи більш загальну ієрархію класів, ви можете просто використовувати object
або iterator
.
Швидке запитання: з точки зору дизайну, чому в C ++ немає базового класу, який зазвичай є object
іншими мовами?
typedef
s. Маючи більш загальну ієрархію класів, ви можете просто використовувати object
або iterator
.
Відповіді:
Остаточне рішення міститься в поширених запитаннях про Stroustrup . Коротше кажучи, це не передає жодного семантичного значення. Це матиме вартість. Шаблони корисніші для контейнерів.
Чому C ++ не має універсального класу Object?
Він нам не потрібен: загальне програмування в більшості випадків забезпечує безпечні альтернативи для статичного типу. Інші справи розглядаються з використанням багаторазового успадкування.
Немає корисного універсального класу: справді універсальний не має власної семантики.
"Універсальний" клас заохочує неакуратно думати про типи та інтерфейси та призводить до перевірки надлишку часу роботи.
Використання універсального базового класу передбачає витрати: об’єкти повинні бути розподілені за купою, щоб бути поліморфними; що передбачає вартість пам'яті та доступу. Об’єкти купи не підтримують, природно, семантику копіювання. Об’єкти купи не підтримують просту поведінку з масштабом (що ускладнює управління ресурсами). Універсальний базовий клас заохочує використовувати Dynamiccast та інші перевірки часу виконання.
Objects must be heap-allocated to be polymorphic
- Я не вважаю це твердження загалом правильним. Ви безумовно можете створити екземпляр поліморфного класу в стеку і передати його як вказівник на один із його базових класів, даючи поліморфну поведінку.
Foo
на-посилання-на- Bar
коли Bar
не успадковує Foo
, детерміновано видає виняток. У C ++ спроба перекинути вказівник на Foo у вказівник на Bar може послати робота назад у часі, щоб вбити Сару Коннор.
Давайте спочатку подумаємо, чому б вам спочатку хотілося мати базовий клас. Я можу придумати кілька різних причин:
Це дві вагомі причини того, що мови торгових марок 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 процедури управління пам’яттю є частиною реалізації класу і успадковуються від базового класу, управління пам’яттю в 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
були б відносно незначними.
Проблема в тому, що в С ++ є такий тип! Це так 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
(я використовую цей синтаксис як найбільш очевидний загальний нетиповий у цьому контексті), який міг би стояти для будь-якого нетиповий аргумент шаблону перед спеціалізацією.
Отже, загалом цей шаблон не переходить з аргументів шаблону типу на нетипові аргументи шаблону.
Як і у багатьох куточках мови С ++, відповідь, швидше за все, "жоден член комітету про це не думав".
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 та його препроцесором.
С ++ додає ще один мікс:
підхід класу / об'єктів
шаблони
STL
Мені особисто не подобаються деякі речі, які надходять безпосередньо з C на C ++. Одним із прикладів є функція перерахування. Спосіб, яким C # дозволяє розробнику використовувати його, набагато кращий: він обмежує перерахування у власному обсязі, має властивість Count і легко піддається ітерації.
Оскільки C ++ хотів бути ретросумісним із C, дизайнер дуже дозволив дозволити мові C увійти цілком до C ++ (є деякі тонкі відмінності, але я не пам'ятаю жодної речі, яку ви могли б зробити за допомогою компілятора C, який ви не вдалося використати компілятор C ++).