new
new运算符意味着在堆上分配内存空间,并将这块内存的管理权(责任)交给用户,意味着我们需要手动释放堆上的内存。
异常安全的类
异常安全简单而言就是:当你的类抛出异常后,你的程序会不会爆掉。爆掉的情况主要包括:内存泄漏,以及不一致的类状态(例如一个字符串类,它的size()方法返回的字符串大小与实际的字符串大小不同),这里仅讨论内存泄漏的情况。
// RAII 风格的类
class TooSimple {
private:
Resource *a;
Resource *b;
public
TooSimple() {
a = new Resource();
b = new Resource(); //在这里抛出异常
}
~TooSimple() {
delete a;
delete b;
}
};
一个函数(或方法)抛出异常,那么它首先将当前栈上的变量全部清空(unwinding),如果变量是类对象的话,将调用其析构函数,接着,异常来到call stack的上一层,做相同操作,直到遇到catch语句。
b = new Resource() 句抛出异常,那么构造函数将被强行终止,指针a,b从call stack上清除,由于此时构造函数还未完成,所以TooSimple的析构函数也不会被调用,a已经被分配了资源,但是call stack被清空,地址已经找不到了,于是delete永远无法执行,于是内存泄漏发生了。
template <typename T>
class StupidPointer {
public:
T *ptr;
StupidPointer(T *p) : ptr(p) {}
~StupidPointer() { delete ptr; }
};
TooSimple() {
a = StupidPointer<Resource>(new Resource());
b = StupidPointer<Resource>(new Resource());
};
此时的a,已经不再是指针,而是StupidPointer
C++11的标准库提供了两种解决问题的思路:1、不允许多个对象管理一个指针(unique_ptr);2、允许多个对象管理同一个指针,但仅当管理这个指针的最后一个对象析构时才调用delete(shared_ptr)。这两个思路的共同点是:只允许delete一次。
C++11结合 RAII ,采用代理模式的思想,设计了三个智能指针,分别为 std::unique_ptr,std::shared_ptr,std::weak_ptr。
求值顺序
f(new A, new B); // 内存泄露
f(shared_ptr<A>(new A), shared_ptr<B>(new B)); // 同样有内存泄露的风险
求值任何表达式的任何部分,包括求值函数参数的顺序都是未说明的。编译器能以任何顺序求值任何操作数和其他子表达式,并且可以在再次求值同一表达式时选择另一顺序。
std::unique_ptr