Нет совершенно никаких причин для того, чтобы в абстрактном классе все функции были виртуальными. Например, существует классический паттерн, когда при помощи абстрактного класса реализуют некий "скелет" общего алгоритма, а при помощи виртуальных функций заполняют уже частные детали конкретного применения этого алгоритма. Детали алгоритма реализуются виртуальными функциями, а сам алгоритм - невиртуальной функцией. Элементарным примером такого паттерна может быть некий класс "Программа":
Поведение виртуальных функций в языке С++ фундаментально основано на понятии динамического типа объекта. Виртуальный вызов функции - это по определению вызов, при котором конкретная версия функции выбирается на основе анализа динамического типа объекта, использованного в этом вызове. По этой причине виртуальный вызов возможен только тогда, когда динамический тип объекта уже определен (проинициализирован, сформирован, зафиксирован - выберете тот термин, который вам нравится). Так вот конструктор - это именно та специальная функция, которая формирует, инициализирует тип объекта. До того момента, как конструктор отработал, объекта еще не существует и тип объекта еще не определен. Т.е. понятие виртуального вызова к такому "сырому" объекту еще не применимо. По этой причине в С++ не может быть виртуальных конструкторов.
Другими словами, в С++ конструктор - эта та самая функция, которая инициализирует механизм виртуальных вызовов. Поэтому сам конструктор вызвать виртуально невозможно - на этот момент функциональность виртуальных вызовов еще не проинициализирована.
Физически, в повсеместно используемой реализации полиморфизма, функциональность виртуальных функций обеспечивается так называемой таблицей виртуальных функций, указатель на которую хранится в каждом полиморфном объекте. Так вот конструктор, кроме прочего, как раз таки и занимается привязкой каждого полиморфного объекта к его правильной таблице виртуальных функций. До того, как такая привязка выполнена, виртуальность работать не может.
Хм... Совершенно верно у каждого класса - свой деструктор. Но виртуальность нужна именно для того, чтобы при полиморфном удалении объекта был правильно выбран именно правильный деструктор, в соответствии с динамическим типом удаляемого объекта. Т.е. если у вас есть указатель на базовый класс Base *p, который на самом деле указывает на объект наследного типа Derived
Base *p = new Derived;
то при выполнении delete p (полиморфное удаление) вам нужно, чтобы был вызван именно деструктор класса Derived. Чтобы это произошло, деструктор должен быть виртуальным.
Если деструктор в такой ситуации невиртуален, то, как правильно заметил @VladD в комментариях, поведение программы не определено.
Создать самостоятельный экземпляр абстрактного класса нельзя потому, что спецификация языка это запрещает. Да и какой смысл в этом создании? Абстрактный класс - это класс с "несуществующими" (ненаписанными) виртуальными функциями. Зачем и кому могут понадобиться объекты такого неполноценного класса? Что вы предлагаете делать, если пользователь попробует вызвать такую несуществующую виртуальную функцию? Вызывать неопределенное поведение? Авторы языка решили, что разумнее просто запретить создание самостоятельных экземпляров абстрактных классов.
В то же время можно сказать, что экземпляры абстрактных классов все таки можно создать, но только как базовые подобъекты неабстрактных классов-наследников, а не как самостоятельные объекты. Собственно, в этом и заключается назначение абстрактных классов - служить в качестве базовых классов для классов-наследников.
Тут еще можно добавить, что таки есть лазейка, позволяющая попытаться выполнить вызов несуществующей виртуальной функции абстрактного класса: это вызов виртуальной функции из конструктора (или деструктора) абстрактного класса. Чтобы "обмануть" компилятор такой вызов обычно приходится делать через промежуточную функцию
struct S {
virtual void foo() = 0;
void bar() { foo(); }
S() { bar(); }
};
struct D : S {
virtual void foo() {}
};
int main() {
D d;
}
Вышеприведенный код приводит к неопределнному поведению (практически - к падению программы) именно из-за того, что во время работы конструктора класса S делается попытка вызова несуществующего метода S::foo(). Можно условно сказать, что в течение того "короткого мига", когда работает конструктор (или деструктор) абстрактного класса, соответствующий объект является самостоятельным абстрактным объектом со всеми вытекающими последствиями, как, например, падение программы при попытке вызова несуществующей виртуальной функции.
Не понимаю, почему возникает такой вопрос. Смысл писать конструктор в абстрактном классе, разумеется, есть, если для такого конструктора в этом классе есть работа. Конструктор должен что-то инициализировать. У вас есть что инициализировать в вашем абстрактном классе? Если есть - то пишите конструктор.
Другое дело, что обычно в абстрактных класса инициализировать нечего. Соответственно и конструктор чаще всего не нужен.
Не понимаю сути вопроса. Дружественность не имеет никакого отношения к виртуальности. Поэтому объявлять виртуальную функцию другом имеет ровно тот же смысл, что и объявлять любую другую (невиртуальную) функцию другом.
Тут стоит еще раз повторить, что дружественность ничего не знает о виртуальности (а виртуальность - о дружественности). Дружественность не распространяется по иерархии классов, т.е. объявленная вами дружественность никак не будет распространяться на "ту же самую" виртуальную функцию в классах-наследниках.
Derived. Чтобы это произошло, деструктор должен быть виртуальным_» наталкивает на неправильную мысль, будто самое плохое, что может произойти — это то, что деструктор классаDerivedне будет вызван. А реальность куда мрачнее. – VladD Jun 08 '15 at 19:29