} catch (...) {
delete dev1_; // Это сработает в любом случае
throw;
}
}
Итак, вышесказанное можно подытожить следующим образом: если вам необходимо использовать члены-указатели, инициализируйте их значением NULL
в списке инициализации и затем выделяйте в конструкторе память для соответствующих объектов, используя блок try/catch
. Вы можете освободить любую память в catch
-обработчике. Однако, если допускается работа с автоматическими членами, сконструируйте их в списке инициализации и используйте специальный синтаксис блока try/catch
для обработки любых исключений.
Рецепт 9.2.
9.4. Создание безопасных при исключениях функций-членов
Создается функция-член и необходимо обеспечить базовые и строгие гарантии ее безопасности при исключениях, а именно отсутствие утечки ресурсов и то, что объект не будет иметь недопустимое состояние в том случае, если выбрасывается исключение.
Необходимо выяснить, какие операции могут выбрасывать исключения, и следует выполнить их первыми, обычно заключая в блок try/catch
. После того как будет выполнен программный код, который может выбрасывать исключение, вы можете изменять состояние объектов. В примере 9.4 показан один из способов обеспечения безопасности функции-члена при исключениях.
class Message {
public:
Message(int bufSize = DEFAULT_BUF_SIZE) :
bufSize_(bufSize), initBufSize_(bufSize), msgSize_(0), buf_(NULL) {
buf_ = new char[bufSize];
}
~Message() {
delete[] buf_;
}
// Добавить в конец символьные данные
void appendData(int len, const char* data) {
if (msgSize_+len > MAX_SIZE) {
throw out_of_range("Data size exceeds maximum size.");
}
if (msgSize_+len > bufSize_) {
int newBufSize = bufSize_;
while ((newBufSize *= 2) < msgSize_+len);
char* p = new char[newBufSize]; // Выделить память
// для нового буфера
copy(buf_, buf_+msgSize_, p); // Скопировать старые данные
copy(data, data+len, p+msgSize_); // Скопировать новые данные
msgSize_ += len;
bufSize_ = newBufSize;
delete[] buf_; // Освободись старый буфер и установить указатель на
buf_ = p; // новый буфер
} else {
copy(data, data+len, buf_+msgSize_);
msgSize_ += len;
}
}
// Скопировать данные в буфер вызывающей программы
int getData(int maxLen, char* data) {
if (maxLen < msgSize_) {
throw out_of_range("This data is too big for your buffer.");
}
copy(buf_, buf_+msgSize_, data);
return(msgSize_);
}
private:
Message(const Message& orig) {} // Мы рассмотрим эти операторы
Message& operator=(const Message& rhs) {} // в рецепте 9.5
int bufSize_;
int initBufSize_;
int msgSize_;
char* buf_;
};
Представленный в примере 9.4 класс Message
является классом, содержащим символьные данные; вы могли бы использовать его в качестве оболочки текстовых или бинарных данных, которые передаются из одной системы в другую. Здесь нас интересует функция-член appendData
, которая добавляет данные, переданные вызывающей программой, в конец данных, уже находящихся в буфере, причем увеличивая при необходимости размер буфера. Здесь обеспечивается строгая гарантия безопасности этой функции-члена при исключениях, хотя на первый взгляд может быть не совсем понятно, чем это достигается.
Рассмотрим следующий фрагмент appendData
.
if (msgSize_+len > bufSize_) {
int newBufSize = bufSize_;
while ((newBufSize *= 2) < msgSize_+len);
char* p = new char[newBufSize];