По словам Joshua Bloch Эффективный Java (книга, которой нельзя рекомендовать достаточно, и которую я купил благодаря непрерывным упоминаниям на stackoverflow):
значение 31 было выбрано, потому что это - нечетное начало. Если бы это было даже и переполненное умножение, информация была бы потеряна, поскольку умножение 2 эквивалентно смещению. Преимущество использования начала менее ясно, но это традиционно. Хорошее свойство 31 - то, что умножение может быть заменено сдвигом и вычитанием для лучшей производительности:
31 * i == (i << 5) - i
. Современные VMs делают этот вид оптимизации автоматически.
(из Главы 3, Объект 9: Всегда переопределяйте хэш-код, когда Вы переопределяете, равняется, страница 48)
Это связано с тем, что причина виртуального метода в том, что вы хотите использовать полиморфизм. Это означает, что вы вызовете метод на указателе базового класса и вам нужна наиболее производная реализация - в этом весь смысл полиморфизма.
Теперь, если у вас не было виртуального деструктора, и через указатель на базовый класс вы вызываете деструктор, вы завершаете вызов деструктора базового класса. В этом случае вы хотите, чтобы полиморфизм работал и с вашим деструктором, например, вызывая деструктор в вашем базовом классе, вы хотите в конечном итоге вызвать деструктор вашего самого производного класса, а не вашего базового класса.
class A
{
virtual void f() {}
~A() {}
}
class B : public A
{
void f() {}
~B() {}
}
A * thing = new B();
thing->f(); // calls B's f()
delete thing; // calls ~A(), not what you wanted, you wanted ~B()
наличие виртуального ~ A () включает полиморфизм.
virtual ~A() {}
Итак, когда вы сейчас вызываете
delete thing;
, будет вызываться ~ B ().
Вы должны объявить виртуальные деструкторы, когда вы проектируете класс как интерфейс, например, вы ожидаете этого будут расширены или внедрены. Хорошая практика в этом случае - иметь класс интерфейса (в смысле интерфейсов Java) с виртуальными методами и виртуальным деструктором, а затем иметь конкретные классы реализации.
Вы можете видеть, что классы STL не имеют виртуальных деструкторов, поэтому они не должны расширяться (например, std :: vector, std :: string ...). Если вы расширяете std :: vector и вызываете деструктор базового класса через указатель или ссылку, вы определенно не вызовете деструктор своего специализированного класса, что может привести к утечке памяти.
Из Часто задаваемые вопросы о стиле и методах C ++ Страуструпа :
Итак, когда мне следует объявлять деструктор виртуальный? Всякий раз, когда в классе хотя бы одна виртуальная функция. Имея виртуальные функции указывают, что класс предназначен для работы в качестве интерфейса в производные классы, и когда это так, объект производного класса может быть уничтожен указателем на base.
Много дополнительной информации о , когда ваш деструктор должен быть виртуальным, в C ++ FAQ . (спасибо Стобору)
Что такое виртуальный член? Из FAQ по C ++ :
[20.1] Что такое «виртуальная функция-член»?
С точки зрения объектно-ориентированного подхода это единственная наиболее важная особенность C ++: [6.9], [6.10].
Виртуальная функция позволяет получить классы для замены реализации предоставляется базовым классом. В компилятор гарантирует, что замена всегда вызывается всякий раз, когда объект в вопрос фактически о производных класс, даже если к объекту обращаются базовым указателем, а не производный указатель. Это позволяет алгоритмы в базовом классе должны быть заменяется в производном классе, даже если пользователи не знают о производных class.
Производный класс может полностью заменить ("переопределить") базовый класс функция-член или производный класс может частично заменить («увеличить») функция-член базового класса. Последний достигается за счет производного функция-член класса вызывает базу при желании функцию-член класса.
Если вы будете (или даже можете) уничтожить объекты производный класс через указатель базового класса, вам понадобится виртуальный деструктор.
Я исхожу из того, что если я собираюсь унаследовать от класса ВООБЩЕ, то у него должен быть виртуальный деструктор. Фактически в коде, который я пишу, нет случаев, когда влияние виртуального деструктора на производительность сказывается, и даже если он на самом деле не нужен сегодня, он может понадобиться ему в будущем, когда класс будет изменен.
В основном: помещайте virtual во все деструкторы базового класса, если у вас нет веской, хорошо продуманной причины не делать этого.
Это просто еще одно практическое правило, но оно удерживает вас от ошибок в дальнейшем.
Всегда.
Если меня не беспокоят накладные расходы на хранилище и производительность vtable, я всегда делаю их виртуальными. Если у вас нет инструмента статического анализа, чтобы убедиться, что ваш деструктор является виртуальным в нужных случаях, не стоит делать ошибку и не создавать виртуальный деструктор, когда это необходимо.
Объект базового класса должен иметь виртуальный деструктор, когда базовый класс должен выполнить свою собственную очистку. Это означает, что если вы выделили ресурсы в базовом классе, для базового класса необходимо выполнить очистку, объявив его деструктор виртуальным, вы гарантируете, что эта очистка будет выполнена (при условии, что вы правильно написали очистку).
В В общем, методы могут быть определены как виртуальные в базовом классе, это позволит производным классам переопределять виртуальные методы, реализуя их собственную производную конкретную реализацию. Я считаю, что это наиболее наглядно демонстрируется на простом примере. Скажем, у нас есть базовый класс Shape, теперь все производные классы должны иметь возможность рисовать. Объект 'Shape' не знает, как рисовать классы, производные от него, поэтому в 'Shape' class мы определяем виртуальную функцию рисования. т.е. (виртуальная пустота draw ();). Теперь в каждом базовом классе мы можем переопределить эту функцию, реализовав определенный код рисования (т.е. квадрат рисуется иначе, чем круг).
Недавно я пришел к выводу, что полностью правильный ответ таков:
Рекомендация №4: Деструктор базового класса должен быть публичным и виртуальным, или защищенный и невиртуальный.
И, конечно же, Херб Саттер дает обоснование своим утверждениям. Обратите внимание, что он выходит за рамки обычных ответов «когда кто-то удалит объект производного класса с помощью указателя базового класса» и «сделает ваш деструктор виртуальным, если в вашем классе есть какие-либо виртуальные функции».