Как реализовать правильную размотку стека и raii, когда выдается bad_alloc во время построения

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

Вот что у меня есть:

class foo{
public:
foo(): var1(nullptr), var2(nullptr){
try{
var1 = new int(1);
var2 = new int(2);
}
catch(std::exception &e){
delete var1;
delete var2;
return;
}
//Some other things involving these variables
}

Прав ли я, имея этот возврат в блоке catch, если я просто хочу прекратить конструирование этого объекта до того, как какой-нибудь более поздний код в конструкторе вызовет ошибку из-за исключения bad_alloc? Или блок catch просто отменяет конструктор после его завершения? Я слишком усложняю все это?

1

Решение

Вы должны проверить умные указатели: std::unique_ptr (тот, который был бы полезен в этом случае) и std::shared_ptr/std::weak_ptr (не правильный тип для вашего примера, но полезно знать).

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

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

0

Другие решения

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

Затем вы должны привести свой код в соответствие с идеей «один класс контролирует один ресурс или выполняет одну работу»

#include <memory>

// foo has one job - to manage fooness.
// It no longer manages memory resources

class foo
{
public:
foo()
: var1(new int(1))
, var2(new int(2))
{
}

std::unique_ptr<int> var1;   // unique_ptr only manages a resource
std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

другие правильные формы инициализации:

#include <memory>

// foo has one job - to manage fooness.
// It no longer manages memory resources

class foo
{
public:
foo()
: var1(std::make_unique<int>(1))
, var2(std::make_unique<int>(2))
{
}

std::unique_ptr<int> var1;   // unique_ptr only manages a resource
std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

значения по умолчанию:

#include <memory>

// foo has one job - to manage fooness.
// It no longer manages memory resources

class foo
{
public:
// default constructor now implicit

std::unique_ptr<int> var1 = std::make_unique<int>(1);   // unique_ptr only manages a resource
std::unique_ptr<int> var2 = std::make_unique<int>(2);   // unique_ptr only manages a resource
};

Правильная, но идиоматически неприятная — избыточная инициализация:

#include <memory>

// foo has one job - to manage fooness.
// It no longer manages memory resources

class foo
{
public:
foo()
: var1(nullptr)
, var2(nullptr)
{
var1 = std::make_unique<int>(1);
var2 = std::make_unique<int>(2);
}

std::unique_ptr<int> var1;   // unique_ptr only manages a resource
std::unique_ptr<int> var2;   // unique_ptr only manages a resource
};

Вот один из способов сделать это без композиции. Обратите внимание на все дополнительные сложности без выгоды:

#include <memory>

// foo now has two jobs - to manage fooness, and to manage resources.
// This adds un-necessary complication, bugs and maintenance overhead

class foo
{
public:
foo()
: var1(nullptr)
, var2(nullptr)
{
var1 = new int(1);
var2 = new int(2);
}

foo(const foo& other)
: var1(nullptr)
, var2(nullptr)
{
if (other.var1) {
var1 = new int(*(other.var1));
}

if (other.var2) {
var2 = new int(*(other.var2));
}
}

foo(foo&& other) noexcept
: var1(other.var1)
, var2(other.var2)
{
other.var1 = nullptr;
other.var2 = nullptr;
}

foo& operator=(const foo& other)
{
foo tmp(other);
std::swap(var1, tmp.var1);
std::swap(var2, tmp.var2);
return *this;
}

foo& operator=(foo&& other) noexcept
{
foo tmp(std::move(other));
std::swap(var1, tmp.var1);
std::swap(var2, tmp.var2);
return *this;
}

~foo() noexcept
{
delete var1;
delete var2;
}

int* var1;   // doesn't manage anything
int* var2;   // doesn't manage anything
};
0

По вопросам рекламы [email protected]