Инструменты пользователя

Инструменты сайта


// На заметку: чисто виртуальные функции с определением

Да, речь про такое:

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].

Такое нужно:

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

Например:

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

Т.е. тело у деструктора должно быть всегда (ну не совсем, см. ниже). Так когда возникает необходимость сделать деструктор базового класса чисто виртуальным с определением? Мне в голову пришло только:

  1. сделать класс чисто виртуальным, не объявляя ни одной чисто виртуальной функции.
  2. сделать класс только со статическими членами и запретить создание объектов этого типа, помогает защититься от дружественных классов и функций и просто от наследования (помещение конструктора по умолчанию в приватную секцию не спасёт от френдов). В этом случае, кстати, делать определение для чисто виртуального деструктора не обязательно.

Поясню. Если сделать так:

struct Virtuable {
  virtual ~Virtuable() {}
};

Все классы-наследники автоматом будут с виртуальными деструкторам, но при этом мы можем сделать такое:

Virtuable f;
Virtuable *p = new Virtuable;

Т.е. мы можем создать объекты-пустышки. А зачем нам это? Если же объявить класс так:

struct Virtuable {
  virtual ~Virtuable() = 0;
};
 
Virtuable::~Virtuable() {}

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

Имхо, может быть полезно во всяких фабриках.

Комментарии