Приведет ли совместимость типа производного класса к базовому классу к утечке памяти? - PullRequest
1 голос
/ 06 января 2011

Я понимаю, что производный класс совместим по типу с указателем на его базовый класс. В данном примере кода new bar построение объекта происходит, вызывая foo::foo(), за которым следует bar::bar(). В соответствующих конструкторах я выделяю ресурсы для членов класса foo::int *a и bar::int *b.

Теперь я инициализирую таким образом построенный объект типом базового класса. С obj я могу вызвать деструктор базового класса, но не деструктор производного класса. Итак, как я могу освободить ресурсы производного класса в этом случае? Разве это не утечка памяти?

#include <iostream>
class foo
{
    int *a;
public:
    foo()
    {
        a = new int[5];
        std::cout << "\n foo constructor" << std::endl;
    }
    ~foo()
    {
        std::cout << "\n foo destructor" << std::endl;
        delete[] a;
    }
};

class bar : public foo
{
    int *b;
public:
    bar()
    {
        b = new int[5];
        std::cout << "\n bar constructor" << std::endl;
    }
    ~bar()
    {
        std::cout << "\n bar destructor" << std::endl;
        delete[] b;
    }
};

int main()
{
    foo *obj = new bar; // Derived class object is type compatible with base class

    delete obj; // Equivalent to obj->~foo();
    return 0;
}

Спасибо.

Ответы [ 4 ]

4 голосов
/ 06 января 2011

Здесь и возникает идея «виртуального деструктора». Технически говоря, если вы удаляете объект через указатель типа базового класса, вы должны пометить этот деструктор базового класса как виртуальный, или результат не определен. Если вы пометите деструктор как виртуальный, значение будет отличаться от других виртуальных функций. Вместо значения «производные классы переопределяют это поведение», оно означает «при удалении этого объекта через указатель базового класса вызывайте производные деструкторы перед вызовом базового конструктора». Это поведение, которое вы хотите.

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

3 голосов
/ 06 января 2011

Это на самом деле неопределенное поведение.

из стандартных документов. 5.3.5.3 Удалить ,

В первом варианте (удаление объекта), , если статический тип операнда отличается от его динамического типа, статический тип. должен быть базовым классом динамического типа операнда, а статический тип должен иметь виртуальный деструктор или поведение undefined. ......

3 голосов
/ 06 января 2011

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

Если объявлено ~foo() virtual, вы можете идти. Сначала будет вызван ~bar(), затем ~foo().

0 голосов
/ 06 января 2011

Do this,

virtual ~foo()
{
   //your code
}

Это гарантирует, что выполнение delete *pFoo также вызовет деструктор производных классов (~bar()).Порядок вызова будет ~bar(), за которым следует ~foo().


Также будет хорошо, если вы сделаете то же самое для ~bar(), то есть

virtual ~bar()
{
   //your code
}

Хотя для этого сценария это не так уж и необходимо, если вы не хотите дальше наследовать от bar и хотите использовать bar* для его производных классов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...