条款13:以对象管理资源
(一)使用RAII
a) 使用RAII(resource acquisition is initialization)(资源获取时机就是初始化时机),即在构造函数中获取资源,在析构函数中释放资源。自己编写资源管理类时,要加上引用计数,引用计数不为0时,不能释放对象,不加引用计数在多线程下会产生析构的竞态。
(二)C++ RAII应用实例:用于管理资源的三个智能指针(其实是三个类模板)
a) auto_ptr(unique_ptr)
i. 使用:auto_ptr<ClassName> ptr(new ClassName());
ii. 在构造ptr时,在其构造函数中获取资源className的对象(放在堆上)
iii. 在超出ptr范围时,在其析构函数中调用delete销毁堆上的对象
iv. 【存在问题】:复制存在问题,再次定义一个auto_ptr<ClassName> ptr2(ptr),那么前一个ptr会指向NULL,即auto_ptr不允许存在一个以上的auto_ptr指向同一个对象。
b) shared_ptr(控制对象的生命期)
i. 和普通指针一样,通过【(*ptr).对象方法】或【ptr->对象方法】来访问对象的成员变量和成员函数。
ii. 和auto_ptr相似也是c++ RAII的应用实例,用来管理资源。不同的是shared_ptr拥有一个引用计数,它不存在auto_ptr存在的问题,即shared_ptr复制动作是正常的,它允许多个shared_ptr指向同一个对象,并且在引用计数为0的时候,调用自己的析构函数,释放获取的资源。(只要有一个shared_ptr指向对象,对象就不会被释放掉)
iii. 但它存在,引用计数的循环引用问题。
1. 引用计数的循环引用问题
1. class Woman;
2. class Man{
3. std::shared_ptr _wife;
4. public:
5. void setWife(std::shared_ptr woman){
6. _wife = woman;
7. }
8. void doSomthing(){
9. if(_wife.lock()){}
10. }
11. ~Man(){ std::cout << "kill man\n"; }
12. };
13. class Woman{
14. std::shared_ptr _husband;
15. public:
16. void setHusband(std::shared_ptr man){
17. _husband = man;
18. }
19. ~Woman(){ std::cout <<"kill woman\n"; }
20. };
21. int main(int argc, char** argv){
22. std::shared_ptr m(new Man());std::shared_ptr w(new Woman());
23. m->setWife(w);w->setHusband(m);
24. }
相互引用,由于shared_ptr管理对象生命周期,所以,都不能正常释放掉husband的生命期由wife的生命期决定,wife的生命期由husband的生命期决定。
2. 解决方案
a) 将woman和man中的其中一个shared_ptr 改成weak_ptr就行
c) weak_ptr(不控制对象的生命期)
i. 不控制对象的生命期,但是知道对象仍然活着
ii. weak_ptr和shared_ptr搭配不会出现引用循环的情况。Weak_ptr本身不能访问对象。weak_ptr.lock()方法返回了:1.若对象为空,返回NULL型shared_ptr;2.若对象非空返回非NULL的shared_ptr,然后又shared_ptr来操作对象。
条款14:在资源管理类中小心copying行为
(一)RAII对象的复制与问题引出
a) 复制RAII对象时,可以选择一并复制RAII管理的资源,也可以选择不产生所管理资源的副本。
b) RAII类管理mutex,mutex锁住,不希望复制资源mutex
Class MRAII{ Mutex *mutexptr; Public: MRAII(Mutex* ptr):mutexptr(ptr){ Lock(mutexptr); } ~MRAII(){ ~lock(mutexptr); } }; Mutex m;//定义互斥量 MRAII m1(&m);//锁住互斥量 MRAII m2(m1);//复制管理类
这样两个mutex指针都指向了mutex对象。一个指针如果释放掉锁,另一个就成了指向不明。
(二)解决方案
a) 禁止复制,将复制构造函数和赋值运算私有化。
b) 将原生指针mutex*替换成shared_ptr,防止上述现象产生,另外shared_ptr的引用计数为0时,会删除资源对象m,这是不愿意见到的,所以需要重新指定shared_ptr的删除器
c) 深拷贝,复制原生指针mutex*所指的对象
d) 使用auto_ptr,转移资源的拥有权
条款15:在资源管理类中提供对原始资源的访问
(一)RAII类是为了防止资源泄漏,不是为了封装资源。
(二)存在直接访问原生资源的情况
a) 显式转换,shared_ptr和auto_ptr都具有get方法,返回原生资源
b) 隐式转换,【ptr->被管理对象的方法】或者【(*ptr).被管理对象的方法】
条款16:成对使用new和delete时要采用相同形式
(一)New和delete配套,new[]和delete[]配套
(二)New:申请一块内存,调用构造函数构造一个对象;delete:析构一个对象,然后释放一块内存。New[]:申请一块内存,构造一组对象(对象数组);delete[]:析构一组对象,然后释放这块内存。所以new、delete[]不能串用;new[]、delete也不能串用。
条款17:以独立语句将newed对象置入智能指针
就是:Shared_ptr<ClassName> ptr(new ClassName());
解释: