Effective C++学习笔记(二)
资源管理
1.以对象管理资源
手动删除:
当factory函数发生异常,或者{...}代码段发生过早的return语句等提前跳出f()函数,则不会执行delete语句,从而造成内存泄露
auto_ptr:
获取对象时立即放进管理对象,管理对象利用析构函数确保资源被释放。由于auto_ptr被销毁时会自动删除所指之物,所以应避免多个auto_ptr指向同一个对象,为了防止这一个问题,auto_ptrs有一个性质:通过copying构造函数或copy assignment操作符复制他们是,它们会变为null,而复制之后的指针将获得该资源的唯一拥有权。
tr1::shared_ptr :
shared_ptr与auto_ptr很类似,但可以允许多个该指针指向同一个资源,其内部能持续追踪有多少个指针指向某笔资源,直到确保没有指针指向它时会自动删除。
2.在资源管理类中小心copying行为
#include <iostream>
using namespace std;
class Lock
{
public:
explicit Lock(int* pm): m_p(pm)
{
lock(m_p);
}
~Lock()
{
unlock(m_p);
}
private:
int *m_p;
void lock(int* pm)
{
cout << "Address = " << pm << " is locked" << endl;
}
void unlock(int *pm)
{
cout << "Address = " << pm << " is unlocked" << endl;
}
};
int main()
{
int m = 5;
Lock m1(&m);
}
运行结果:
m1获得资源时将之锁住,释放资源时将之解锁,若修改main()函数,
int main()
{
int m = 5;
Lock m1(&m);
Lock m2(m1);
}
此时运行结果发生改变,
我们发现发生了一次locked和两次unlocked,原因是生成m1时调用一次含有locked构造函数,并最后释放一次,生成m2时调用默认copying构造函数并释放一次,且发生的是浅拷贝,两次释放同一个资源,在对于对内存操作的代码中会造成程序崩溃。
可以通过以下方法改善:
- 通过构造私有的拷贝构造函数和赋值操作符来禁止此类操作;
private:
Lock(const Lock&);
Lock& operator= (const Lock&);
- 通过继承uncopyable类;
class Lock: public Uncopyable
{…}
- 使用shared_ptr,引用计数器
class Lock
{
public:
explicit Lock(int *pm): m_p(pm, unlock){…}
private:
shared_ptr<int> m_p; //采用shared_ptr
}
- 复制底部资源(深拷贝)
- 转移控制权
class Lock
{
public:
explicit Lock(int *pm): m_p(pm, unlock){…}
private:
auto_ptr<int> m_p; //采用auto_ptr
}
3.成对使用new和delete时要采用相同的形式
如下例子:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout<<"constructor A"<<endl;
}
~A() {
cout<<"destructor ~A"<<endl;
}
};
int main() {
A* a1 = new A(); //生成一个指向A对象的指针
A* a2 = new A[3]; //生成一个指向A对象的指针数组
delete a1; //释放a1
delete [] a2; //释放a2
return 0;
}
运行结果如下:
可知执行了4(1+3)次无参构造函数,4次析构函数,若修改主函数
int main() {
A* a1 = new A();
A* a2 = new A[3];
delete a1;
delete a2; //指针数组释放不带[]
return 0;
}
运行结果如下:
此时只调用2次(1+1)析构函数,只释放了指针数组内的第一个资源,再次修改主函数
int main() {
A* a1 = new A();
A* a2 = new A[3];
delete [] a1; //释放指针带[]
delete [] a2; //释放a2
return 0;
}
运行结果如下:
此时能通过编译,但程序陷入死循环,一直调用析构函数,在释放指针时带[ ]程序发生异常
4.以独立语句将newed对象置入智能指针
对于上述函数,编译器执行的顺序可能为: new Widget , priority() , tr1::shared_ptr。此时若priority()函数发生异常,则此时new Widget对象并不能成功置入智能指针中,有可能会发生内存泄露,因此应改为以下方式: