在标准C++库设计过程中异常安全是一个至关重要的考虑因素
当面对异常的时候有不同级别的异常安全
自定义赋值操作符 operator=应该遵守下面的模式
1 确保程序不是给自己赋值。 如果是的话,跳到6
2 给指针数据成员分配所需的新内存
3 从原有的内存区向新分配的内存区拷贝数据
4 释放原有的内存
5 更新对象的状态,也就是把指向分配新堆内存地址的指针赋给指针数据成员
6 返回 *this
重要的是,直到所有的新增部件都被安全地分配到内存并初始化之前不要修改对象
的状态。
在一个拥有两个指针成员 theString 和 theInts 的类使用技术
//: C01:SafeAssign.cpp
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
// An Exception-safe operator=.
#include <iostream>
#include <new> // For std::bad_alloc
#include <cstring>
#include <cstddef>
using namespace std;
// A class that has two pointer members using the heap
class HasPointers {
// A Handle class to hold the data
struct MyData {
const char* theString;
const int* theInts;
size_t numInts;
MyData(const char* pString, const int* pInts,
size_t nInts)
: theString(pString), theInts(pInts), numInts(nInts) {}
} *theData; // The handle
// Clone and cleanup functions:
static MyData* clone(const char* otherString,
const int* otherInts, size_t nInts) {
char* newChars = new char[strlen(otherString)+1];
int* newInts;
try {
newInts = new int[nInts];
} catch(bad_alloc&) {
delete [] newChars;
throw;
}
try {
// This example uses built-in types, so it won't
// throw, but for class types it could throw, so we
// use a try block for illustration. (This is the
// point of the example!)
strcpy(newChars, otherString);
for(size_t i = 0; i < nInts; ++i)
newInts[i] = otherInts[i];
} catch(...) {
delete [] newInts;
delete [] newChars;
throw;
}
return new MyData(newChars, newInts, nInts);
}
static MyData* clone(const MyData* otherData) {
return clone(otherData->theString, otherData->theInts,
otherData->numInts);
}
static void cleanup(const MyData* theData) {
delete [] theData->theString;
delete [] theData->theInts;
delete theData;
}
public:
HasPointers(const char* someString, const int* someInts,
size_t numInts) {
theData = clone(someString, someInts, numInts);
}
HasPointers(const HasPointers& source) {
theData = clone(source.theData);
}
HasPointers& operator=(const HasPointers& rhs) {
if(this != &rhs) {
MyData* newData = clone(rhs.theData->theString,
rhs.theData->theInts, rhs.theData->numInts);
cleanup(theData);
theData = newData;
}
return *this;
}
~HasPointers() { cleanup(theData); }
friend ostream&
operator<<(ostream& os, const HasPointers& obj) {
os << obj.theData->theString << ": ";
for(size_t i = 0; i < obj.theData->numInts; ++i)
os << obj.theData->theInts[i] << ' ';
return os;
}
};
int main() {
int someNums[] = { 1, 2, 3, 4 };
size_t someCount = sizeof someNums / sizeof someNums[0];
int someMoreNums[] = { 5, 6, 7 };
size_t someMoreCount =
sizeof someMoreNums / sizeof someMoreNums[0];
HasPointers h1("Hello", someNums, someCount);
HasPointers h2("Goodbye", someMoreNums, someMoreCount);
cout << h1 << endl; // Hello: 1 2 3 4
h1 = h2;
cout << h1 << endl; // Goodbye: 5 6 7
getchar();
} ///:~
类HasPointers使用MyData类作为两个指针的句柄
一旦需要分配更多 的内存,不论是在构造函数中还是在赋值操作中,程序
最终都会调用第一个clone函数完成任务
没有一个delete操作会抛出异常
只能假设析构函数不抛出异常,否则无法设计异常安全的代码
不要让析构函数抛出异常
输出
Hello: 1 2 3 4
Goodbye: 5 6 7