Что происходит, когда деструктор вызывает абстрактную функцию

У меня возникли проблемы с пониманием причины сбоя в следующем коде:

class A {
public:
    virtual ~A() { goo(); }
    void goo() { absFoo(); }
    virtual void absFoo() = 0;

};

class B : public A {
public:
    void absFoo() { cout << "In B \n"; }
};

int main() 
{
    B b1;
    b1.goo();
}

Основные принты "In B", как и ожидалось, но в конце, когда он падает, я не могу его отлаживать, компилятор выводит странное сообщение.

Поэтому мой вопрос: когда деструктор A называет "goo()", делает ли "absFoo()", сбой, потому что мы имеем в виду абстрактную функцию?

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

Я знаю, что если бы мы вызвали "absFoo()" непосредственно из деструктора, причиной была бы абстрактная функция. Но поскольку здесь это внешняя функция, вызывающая "absFoo()" У меня возникли проблемы с пониманием реальной причины.

Ответ 1

Что происходит, когда деструктор вызывает абстрактную функцию

Во-первых, давайте рассмотрим, что происходит, когда деструктор вызывает любую виртуальную функцию (кстати, это относится и к конструктору): Когда виртуальная функция foo вызывается в деструкторе T, вызов не будет динамически отправляться в реализацию в производный тип (время жизни любого производного объекта уже завершено), но статически для реализации T::foo.

Если T::foo является чисто виртуальным, то вызов его без динамической отправки будет иметь неопределенное поведение. Это то, что происходит, когда чистая виртуальная функция (косвенно) вызывается в деструкторе (или конструкторе).

Ответ 2

Чтобы дополнить уже принятый ответ, это документация из cppreference.

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

Другими словами, во время строительства или разрушения более производные классы не существуют.

Ответ 3

Когда объект деконструируется, vtable обновляется в соответствии с новым статусом объекта.

Поскольку вы удалили последнюю функцию, компилятор сделает все, что считает нужным; который в случае отладочной компиляции в визуальной студии будет отпадать до сообщения о прерывании, которое вызывало бы чистую виртуальную функцию.

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