Использование базового класса в качестве безопасного контейнера для указателей
Итак, я рассматривал этот вопрос Исключение выделения памяти в конструкторе, где мой босс заявляет в своем прекрасном ответе, что деструктор не будет вызван.
Что заставляет меня задуматься,
Если бы я написал
struct XBase
{
int* a;
char* b;
float* c;
XBase() : a(nullptr), b(nullptr), c(nullptr) {}
~XBase()
{
delete[] a; delete[] b; delete[] c;
}
};
и
struct X : XBase
{
X() {
a = new int[100];
b = new char[100];
c = new float[100];
}
}
Затем, если выделение c
завершается с ошибкой (при исключении выбрасывается), тогда будет вызван деструктор XBase
, так как был создан базовый класс.
И нет утечки памяти?
Правильно ли я?
Ответы
Ответ 1
Вы правы; это будет работать, потому что:
- К тому времени, когда выполняется тело конструктора
X
, XBase
уже построено, и его деструктор будет вызываться.
- Выполнение
delete
или delete[]
в нулевых указателях абсолютно корректно и ничего не делает.
Итак, если сбой a
, b
или c
не выполняется, деструктор XBase
освободит все.
Но, очевидно, этот дизайн заставляет вас писать гораздо больше кода, который вам нужен, поскольку вы можете просто использовать std::vector
или std::unique_ptr<T[]>
.
Ответ 2
Просто, чтобы иметь официальную версию для резервного копирования претензий,
& раздел; 15.2/2
Объект любой продолжительности хранения, инициализация или уничтожение которой завершается с помощью исключения, будет иметь деструкторы, выполняемые для всех его полностью построенных подобъектов [...].
Ответ 3
Если вы беспокоитесь о сбое в распределении памяти, я бы поместил его в отдельный метод, например Build
или Create
. Затем вы можете позволить деструктору обработать его, если он был инициализирован (но определенно проверьте указатель перед слепое delete
-ing.
Другим решением являются интеллектуальные указатели, которые не предназначены для этого случая обязательно, но автоматически освобождают их содержимое.