Переопределить Деструктор В C++

из C++ FAQ:

[11.4] могу ли я перегрузить деструктор для моего класса? Нет.

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

class Child : public Parent {
public:
    virtual Parent::~Parent() {
        // New definition
    }
};

и если на то пошло, сделайте это рекурсивно?

class Grandchild : public Child {
public:
    Child::Parent::~Parent() {
        // An even newer definition
    }
};

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

EDIT: я изменил это, чтобы отразить тот факт, что я хочу переопределить деструктор родителя, отметить переопределение Child и Grandchild ~Parent().

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

4 ответов


я, возможно, разделяя волосы на синтаксис слов

нет, вы определенно не – это две совершенно разные вещи.

но можно ли переопределить деструктор?

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

Parent const& p = Child();

будет правильно назвать p.~Child() в конце области, потому что Parent::~Parent - это виртуальный.


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

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

однако существует разница между виртуальными деструкторами и методами виртуальных членов, которая не имеет ничего общего с virtual природа деструктор. То есть при выполнении такого кода:

class A
{
public:  
  virtual void Foo() {}
  virtual ~A() {};
};

class B : public A
{
public:
  void Foo() {};
  ~B() {}
};

int main()
{
  A* a = new B;
  a->Foo();  // B::Foo() is called
  delete a;  // B is destroyed via B::~B()
}

...когда вы звоните a->Foo() метод Foo() на B называется. С B::Foo() явно не вызывает A::Foo(), A::Foo() не называется.

Впрочем, когда объект уничтожается через delete a;, первый деструктор B::~B() вызывается, а затем после этого заканчивается, но до того, как управление вернется в программу, деструктор базового класса A::~A() is и называется.

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

Obligitory Стандартная Цитата:

[C++03] 12.4 / 6: Деструкторы

после выполнения тела деструктора и уничтожать любые автоматические объекты, выделенные внутри тела, деструктор для класса X вызывает деструкторы для прямых членов X, деструкторы для X прямые базовые классы и, если X-тип наиболее производного класса (12.6.2), его деструктор вызывает деструкторы для виртуальной базы X занятия. Все деструкторы вызываются как если бы они были связаны с полным именем, то есть, игнорируя любые возможные виртуальные переопределить деструкторы в производных классах. Базы и члены разрушены в обратном порядке завершения их конструктор (см. 12.6.2). Оператор return (6.6.3) в деструкторе может не возвращаться непосредственно вызывающему абоненту; перед передачей управления вызывающему вызываются деструкторы для членов и баз. Деструкторов для элементов массива вызываются в обратном порядке порядок их строительства (см. 12.6).


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

Это выглядит так:

class Parent {
public:
    virtual ~Parent();
};

class Child : public Parent {
public:
    virtual ~Child();
};

class Grandchild : public Child {
public:
    ~Grandchild(); // virtual is inherited here
};

обратите внимание, что деструктор не переопределяется имя как обычные функции, потому что имя всегда является именем класса, экземпляр которого вы уничтожаете.

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


терминология

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

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

рассмотрим следующий пример:

class A
{
public:
    A() { a = new int; }
    virtual ~A() { delete a; }

private:
    int *a;
};

class B : public A
{
public:
    B() { b = new int; }
    virtual ~B() { delete b; }

private:
    int *b;
};

int main()
{
    A *a = new B();
    delete a;
}

если деструктор был не виртуальный, потом delete a вызовет только деструктор A, и вы получите утечку памяти. Но потому что он виртуальный, оба деструктора будут вызваны, в порядке ~B() ->~A().