Отсутствие виртуального деструктора при выполнении «Удалить это» - PullRequest
3 голосов
/ 16 сентября 2011

Раздел 16.15 из C ++ FAQ Lite обсуждает delete this и затем упоминает:

Естественно, обычные предостережения применяются в тех случаях, когда указатель указатель на базовый класс, когда у вас нет виртуального деструктора.

Почему это правда? Рассмотрим этот код:

class ISuicidal {
public:
    virtual void suicide() = 0;
};

class MyKlass : public ISuicidal {
public:
    MyKlass() {
        cerr << "MyKlass constructor\n";
    }

    ~MyKlass() {
        cerr << "MyKlass destructor\n";
    }

    void suicide() {
        delete this;
    }
};

Используется таким образом:

int main()
{
    ISuicidal* p = new MyKlass;
    p->suicide();
    return 0;
}

В вызове p->suicide() деструктор MyKlass вызывается, как и ожидалось, , хотя у ISuicidal нет виртуального деструктора .

Для меня это имеет смысл, поскольку в MyKlass::suicide статический тип this известен как MyKlass*, поэтому вызывается правильный деструктор. Это легко проверить, разместив typeid звонки внутри suicide.

Так что запись FAQ часто неточна, или я неправильно ее понимаю?

Ответы [ 6 ]

4 голосов
/ 16 сентября 2011

В вашей функции suicide(), Вы используете delete this;
Здесь указатель this соответствует классу MyKlass, так как функция определена в MyKlass, а не ISuicidal, и поэтому деструктор MyKlass называется.

Если бы вы определили функцию в ISuicidal, то она не будет вызывать деструктор MyKlass, если вы не объявите виртуальный деструктор в ISuicidal.

4 голосов
/ 16 сентября 2011

Вы недоразумение. Реализуйте функцию самоубийства (т.е. удалите это) в ISuicidal, и вы обнаружите, что когда указатель this является базовым классом, вызывающим delete для него, он не вызывает деструктор производного класса.

2 голосов
/ 16 сентября 2011

class Child : public MyKlass { ~Child () {} };

ISuicidal* p = new Child;

p->suicide(); // ~Child() not called !

2 голосов
/ 16 сентября 2011

проблема возникает, если вы введете другой реальный класс в иерархию, производную от MyClass (например, MyClass2).

class ISuicidal {
public:
    virtual void suicide() = 0;
};

class MyKlass : public ISuicidal {
public:
    MyKlass() {
       cerr << "MyKlass constructor\n";
    }

    ~MyKlass() {
        cerr << "MyKlass destructor\n";
    }

    void suicide() {
        delete this;
    }
};

class MyKlass2 : public MyKlass {
public:
    MyKlass2() {
        cerr << "MyKlass2 ctr"<<std::endl;
    } 

    ~MyKlass2() {
        cerr << "MyKlass2 dtr"<<std::endl;
    }
}

int main()
{
    MyKlass* p = new MyKlass2;
    delete p; //destructor of base class called, not the destructor of MyKlass2 because
              //the destructor is not virtual
    return 0;
}
0 голосов
/ 16 сентября 2011

Я думаю, вы неправильно это поняли.Проблема возникает, когда вы вызываете delete this в базовом классе, то есть когда указатель this имеет тип указателя на базовый класс.

Естественно, обычные предостережения применяются в тех случаях, когда указатель thisуказатель на базовый класс, когда у вас нет виртуального деструктора.

В вашем примере это не указатель на базовый класс, а скорее указатель на производный класс.

0 голосов
/ 16 сентября 2011

это безопасно, если вы вызываете точный деструктор экземпляра (например, не вызываете деструктор базы).

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

...