Да, речь про такое:
struct Foo {
virtual ~Foo() = 0; // [1]
virtual void proc() = 0; // [2]
};
// [3]
Foo::~Foo() {
// some actions
}
// [4]
void Foo::proc() {
// some actions
}
[3] и [4] для [1] и [2] соответственно могут находится в другом файле.
Информации в интернете много, поэтому заметка для себя, ибо где-то пропустил в изучении. Далее мои домысли и моё понимание проблемы.
Начнём с [2].
Такое нужно: <WRAP center round tip 60%>
- когда требуется, что бы в наследниках эти функции были обязательно определены (т.е. были чисто виртуальными в базовом),
- но при этом требуется, что бы была предоставлена реализация по умолчанию. Например:
struct Bar : Foo {
virtual void proc() {
// здесь полностью своя реализация
}
};
struct Baz : Foo {
virtual void proc() {
// а здесь мы позвали реализацию по умолчанию.
Foo::proc();
}
};
struct Bak : Foo {
// а такой класс мы инстанцировать не сможем
};
А теперь про [1]
Начнём с того, что деструкторы, по иерархии, не переопределяются и при уничтожении объекта вызываются все деструкторы до базового класса и если деструктор будет чисто виртуальным, то получим такую ошибку, ещё на этапе компиляции:
/tmp/cc42YqK5.o: In function `Bar::~Bar()':
main.cpp:(.text._ZN3BarD2Ev[_ZN3BarD5Ev]+0x1f): undefined reference to `Foo::~Foo()'
collect2: error: ld returned 1 exit status
Т.е. тело у деструктора должно быть всегда (ну не совсем, см. ниже). Так когда возникает необходимость сделать деструктор базового класса чисто виртуальным с определением? Мне в голову пришло только: <WRAP center round tip 60%>
- сделать класс чисто виртуальным, не объявляя ни одной чисто виртуальной функции.
- сделать класс только со статическими членами и запретить создание объектов этого типа, помогает защититься от дружественных классов и функций и просто от наследования (помещение конструктора по умолчанию в приватную секцию не спасёт от френдов). В этом случае, кстати, делать определение для чисто виртуального деструктора не обязательно.
Поясню. Если сделать так:
struct Virtuable {
virtual ~Virtuable() {}
};
Все классы-наследники автоматом будут с виртуальными деструкторам, но при этом мы можем сделать такое:
Virtuable f;
Virtuable *p = new Virtuable;
Т.е. мы можем создать объекты-пустышки. А зачем нам это? Если же объявить класс так:
struct Virtuable {
virtual ~Virtuable() = 0;
};
Virtuable::~Virtuable() {}
то, у нас так же все классы-наследники будут иметь виртуальные деструкторы, а вышеизложенный код уже не будет компилироваться, т.е. объекты-пустышки создать не получится.
Имхо, может быть полезно во всяких фабриках.