Этот блок программного кода обеспечивает увеличение размера буфера. Я его увеличиваю путем удвоения его размера до тех пор, пока он не станет достаточно большим. Этот фрагмент программного кода безопасен, потому что исключение может быть выброшено здесь только при выполнении оператора new
bad_alloc
, если операционная система не сможет выделить участок памяти необходимого размера.После успешного распределения памяти я могу начать обновление состояния объекта, копируя данные и обновляя значения переменных-членов.
copy(buf_, buf_+msgSize_, p);
copy(data, data+len, p+msgSize_);
msgSize_ += len;
bufSize_ = newBufSize;
delete[] buf_;
buf_ = p;
Ни одна из этих операций не может выбросить исключение, поэтому нам не о чем волноваться. (Это происходит только из-за того, что буфер представляет собой последовательность символов; дополнительные разъяснения вы найдете при обсуждении примера 9.5.)
Это простое решение и общая стратегия обеспечения строгой безопасности функций- членов при исключениях заключается в следующем: сначала выполняйте все то, что может выбрасывать исключения, затем, когда вся опасная работа окажется выполненной, глубоко вздохните и обновите состояние объекта. appendData
сору
operator=
для каждого элемента копируемой последовательности. В примере 9.4 каждый элемент имеет тип char
, поэтому безопасность обеспечена, так как оператор присваивания одного символа другому не может выбросить никакого исключения. Но я сказал «обеспечит с трудом», потому что безопасность этого специального случая не должна создавать у вас впечатление о том, что причиной исключений никогда не может быть функция copy
.Предположим на секунду, что вместо «узкого» символьного буфера вам необходимо написать класс Message
template
class MessageGeneric {
public:
MessageGeneric(int bufSize = DEFAULT_BUF_SIZE) :
bufSize_(bufSize), initBufSize_(bufSize), msgSize_(0), buf_(new T[bufSize]) {}
~MessageGeneric() {
delete[] buf_;
}
void appendData(int len, const 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);
T* p = new T[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, T* data) {
if (maxLen < msgSize_) {
throw out of range("This data is too big for your buffer.");
}
copy(buf_, buf_+msgSize_, data);
return(msgSize_);
}
private:
MessageGeneric(const MessageGeneric& orig) {}
MessageGeneric& operator=(const MessageGeneric& rhs) {}
int bufSize_;
int initBufSize_;
int msgSize_;
T* buf_;
};