Подтвердить что ты не робот

С++: Почему для struct\class нужен виртуальный метод, чтобы быть полиморфным?

Следуя этому вопросу, мне интересно, почему struct\class в С++ должен иметь виртуальный метод, чтобы быть полиморфным.

Принуждение виртуального деструктора имеет смысл, но если вообще нет деструктора, почему это обязательный виртуальный метод?

4b9b3361

Ответ 1

Поскольку тип полиморфного объекта в С++ в основном определяется из указателя на его vtable, который является таблицей виртуальных функций. Однако vtable создается только при наличии хотя бы одного виртуального метода. Зачем? Потому что в С++ вы никогда не получаете то, о чем явно не просили. Они называют это "вам не нужно платить за то, что вам не нужно". Не нужен полиморфизм? Вы только что сохранили таблицу vtable.

Ответ 2

Принуждение виртуального деструктора имеет смысл

Совершенно верно. Чтобы разрушить виртуальный класс вручную (через delete) через его базовый класс, вам нужен виртуальный деструктор. (Теперь, как мне было напомнили в комментариях, это обычно не требуется: вместо использования ручного управления памятью можно опираться на современные интеллектуальные указатели, которые также корректно работают с не виртуальными деструкторами.)

Таким образом, любой класс, который действует как полиморфный базовый класс, обычно нуждается либо в виртуальном деструкторе, либо в виртуальных функциях.

И так как полиморфизм времени выполнения добавляет накладные расходы (класс должен хранить дополнительный указатель на свою таблицу виртуальных методов), по умолчанию не следует добавлять его, если это не необходимо в любом случае: философия дизайна С++ - "вы платите только за то, что вы необходимость". У каждого класса есть таблица виртуальных методов, которая будет нарушать этот принцип.

Ответ 3

Потому что он определен как таковой в стандарте.

От 10.3/1 [class.virtual]

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

Имеет смысл, что если вы используете наследование, то у вас есть хотя бы один виртуальный метод. Если у вас нет виртуального метода, вы можете использовать композицию вместо этого.

Ответ 4

Полиморфизм

заключается в том, чтобы позволить вашим подклассам переопределять поведение функций базового класса по умолчанию, поэтому, если у вас нет виртуальных методов в вашем базовом классе, вы не можете переопределять методы в базе.

Ответ 5

Мне интересно, почему у struct\class в С++ должен быть виртуальный метод, чтобы быть полиморфным?

Потому что это то, что означает полиморфный класс.

В С++ полиморфизм времени выполнения достигается посредством виртуальных функций. Базовый класс объявляет некоторые виртуальные функции, реализуемые многими производными классами, и клиенты используют указатели (или ссылки) статического типа базового класса и могут указывать на объекты производных классов (часто разные производные классы), а затем позже on, вызовите реализацию производных классов через базовые указатели. Таким образом достигается полиморфизм во время выполнения. А поскольку центральную роль играют функции virtual, что позволяет использовать полиморфизм во время выполнения, поэтому классы, имеющие виртуальные функции, называются полиморфными классами.

Ответ 6

Без какого-либо виртуального метода нет необходимости поддерживать виртуальный указатель (сокращенно vptr) для каждого объекта класса. Виртуальный указатель - это механизм разрешения вызовов виртуальных методов во время выполнения; в зависимости от класса объекта, он может указывать на разные таблицы виртуальных методов (сокращенно vtable), которые содержат фактические адреса виртуальных методов.

Итак, проверяя, что vtable указывает на точку vptr, компилятор может определить класс объекта, например, в dynamic_cast. Объект без vptr не может определить свой тип таким образом и не является полиморфным.

Ответ 7

Философия дизайна на С++ заключается в том, что "вы не платите за то, что не используете". Возможно, вы уже знаете, что функция virtual несет некоторые накладные расходы, поскольку класс должен поддерживать указатель на ее реализацию. Фактически, объект содержит ссылку на таблицу указателей функций, называемую vtable.

Рассмотрим следующий пример:

class Base
{
public:
    virtual f() { /* do something */ }
}; 

class Derived : public Base
{
public:
    virtual f() { /* do something */ }
}; 

Base* a = new Derived;
a->f(); // calls Derived::f()

Обратите внимание, что переменная a указывает на объект Derived. Поскольку f() объявлен virtual, таблица vtable a будет содержать указатель на Derived::f() и реализация выполняется. Если f() не virtual, vtable будет пустым. Таким образом, Base::f() выполняется как тип a is Base.

Деструктор ведет себя точно так же, как и другие функции-члены. Если деструктор не virtual, будет вызван только деструктор в классе Base. Это может привести к утечке памяти/ресурсов, если класс Derived реализует RAII. Если класс предназначен для подкласса, его деструктор должен быть virtual.

На некоторых языках, таких как Java, все методы являются виртуальными. Таким образом, даже объекты, которые не предназначены для полиморфизма, будут потреблять память для поддержки указателей функций. Другими словами, вы вынуждены платить за то, что вы не используете.

Ответ 8

Классам нужны только виртуальные методы, чтобы быть динамически полиморфными - по причинам, описанным другими. Тем не менее, вы все равно можете иметь статический полиморфизм через шаблоны.