Подтвердить что ты не робот

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

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

class Base {
  Resource *r;

public:
  Base() { /* ... */ }
  ~Base() {
    delete r; 
  }
};

class Derived : public Base {
public:
  Derived() { /* ... */ }
  ~Derived() {
    /* Suddenly something here throws! */
  }
};

int main() {
  try {
    Derived d;
  } catch(...) {
    /* what happened with Base::r !? */
  }
}

Будет вызван деструктор базового класса, если вызывается деструктор производного класса? Или будет утечка?

4b9b3361

Ответ 1

Согласно § 15.2/2:

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

Итак, следует вызвать деструктор базового класса. То есть, как мы знаем, это очистит базовый класс:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "clean" << std::endl;
    }
};

struct bar : foo
{
    bar()
    { // foo is initialized...
        throw 0; // ...so its destructor is run
    }
};

int main()
{
    try
    {
        bar b;
    }
    catch (...)
    {
        std::cerr << "caught" << std::endl;
    }
}

И это очистит элемент:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "clean" << std::endl;
    }
};

struct bar
{
    ~bar()
    { // f has been initialized...
        throw 0; // ...so its destructor will run
    }

    foo f;
};

int main()
{
    try
    {
        bar b;
    }
    catch (...)
    {
        std::cerr << "caught" << std::endl;
    }
}

Это также очистит базовый класс:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "clean" << std::endl;
    }
};

struct bar : foo
{
    ~bar()
    { // foo has been initialized...
        throw 0; // ...so its destructor will run
    }
};

int main()
{
    try
    {
        bar b;
    }
    catch (...)
    {
        std::cerr << "caught" << std::endl;
    }
}

Это мое понимание цитаты.

Ответ 2

Будет вызван базовый деструктор.

В Effective С++ Мейерс рекомендует, чтобы исключения не оставляли деструкторов. Поймайте исключение внутри деструктора и обработайте его, проглотите или завершите.

Ответ 3

Деструктор базового класса действительно называется. Пример кода:

#include 
#include 

class Base {
public:
  Base() { /* ... */ }
  ~Base() {
    printf("Base\n");
  }
};

class Derived : public Base {
public:
  Derived() { /* ... */ }
  ~Derived() {
    printf("Derived\n");
    throw 1;
  }
};

int main() {
  try {
    Derived d;
  } catch(...) {
    printf("CAUGHT!\n");
  }
}

Отпечатки:

Derived
Base
CAUGHT!