Когда помечать функцию в С++ как виртуальную?

Из-за природы C++ статического связывания методов это влияет на полиморфные вызовы.

Из Википедии:

Хотя накладные расходы, связанные с этим механизмом диспетчеризации, невелики, они все же могут быть значительными для некоторых областей приложений, для которых предназначен язык. По этой причине Бьерн Страуструп, разработчик C++, решил сделать динамическую диспетчеризацию необязательной и не используемой по умолчанию. Только функции, объявленные с помощью ключевого слова virtual, будут отправлены в зависимости от типа объекта во время выполнения; другие функции будут отправлены на основе статического типа объекта.

Итак, код:

Polygon* p = new Triangle;
p->area();

при условии, что area() является функцией non-virtual в родительском классе, которая является overridden в дочернем классе, приведенный выше код вызовет Parent's class method, чего разработчик может не ожидать. (благодаря статической привязке, которую я представил)

Итак, если я хочу написать класс, который будет использоваться другими (например, библиотека), должен ли я сделать все свои функции виртуальными, чтобы такой предыдущий код работал должным образом?


person Muhammad Hewedy    schedule 28.11.2011    source источник
comment
p.area() даже не компилируется. Может быть, вы имели в виду p->area()? Я бы сказал, пора взять хорошую книгу по C++.   -  person Kerrek SB    schedule 28.11.2011
comment
Это зависит от других вариантов дизайна, которые вы сделали. Как разработчику мне все равно, вызывает ли p->area() Polygon::area(), Triangle::area или какую-то другую функцию, если она возвращает правильный ответ. Это полностью лежит на библиотеке и непрозрачно для пользователя библиотеки.   -  person Robᵩ    schedule 28.11.2011
comment
Вы можете найти эту статью очень полезной.   -  person Björn Pollex    schedule 28.11.2011


Ответы (5)


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

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

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

person Alok Save    schedule 28.11.2011
comment
Я писал огромный ответ, но я думаю, что это подводит итог. - person John Dibling; 28.11.2011
comment
Это означает, что только классы final не должны содержать виртуальные методы? так как остальные классы могут быть унаследованы другими людьми, использующими мой класс - person Muhammad Hewedy; 28.11.2011
comment
@Muhammad: я не уверен, что вы называете классами final, в C ++ нет такого понятия, и я плохо разбираюсь в Java. - person Alok Save; 28.11.2011
comment
в Java класс final (steal в C#) — это класс, который не должен быть подклассом (например, 1- функциональные классы или классы, которые лучше представлены функциями — как если бы методы math.h помещались в какой-то класс, 2- классы, которые не должны быть подклассами по соображениям безопасности, например класс java.lang.String) - person Muhammad Hewedy; 28.11.2011
comment
@Muhammad Это не имеет ничего общего с final. Это вопрос дизайна класса. Если ваш код явно не разработан таким образом, что реализация этой функции может варьироваться (в заданных пределах) без нарушения каких-либо инвариантов класса, тогда эта функция не должна быть виртуальной --- в хорошо написанной Java ( что-то чрезвычайно редкое), большинство функций должны быть объявлены final. Это вопрос дизайна, и пока вы не решите его, слишком рано писать код. - person James Kanze; 29.11.2011
comment
@JamesKanze, пожалуйста, не могли бы вы предоставить мне больше информации об этом моменте: in well written Java (something exceedingly rare), most functions should be declared final. It's a design issue, and until you've settled it, it's too early to be writing code.? - person Muhammad Hewedy; 29.11.2011
comment
@Muhammad Поскольку большинство функций не должны быть виртуальными, а в Java функции виртуальны по умолчанию, из этого следует, что большинство из них должно быть final, чтобы отключить виртуальность. - person James Kanze; 29.11.2011

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

person Pubby    schedule 28.11.2011
comment
Это и не так просто, как это. - person John Dibling; 28.11.2011

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

person James Kanze    schedule 28.11.2011
comment
Еще одним положительным преимуществом сохранения невиртуальных публичных функций является то, что перед отправкой они обеспечивают единую точку останова. - person Kevin Hopps; 29.11.2011

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

person Sean Duggan    schedule 28.11.2011

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

person Jérémy Dutheil    schedule 28.11.2011