学习智能指针

0.历史上的各种内存泄漏检测工具

boundcheck

C++11中智能指针的原理、使用、实现

C++中共享指针是怎么计数的?

说明:

本文选自《C++标准库 (第2版)》

1.智能指针

1.1 初识

C语言让我们知道,pointer很重要,却是麻烦的来源。使用pointer的理由之一是在惯常的作用域边界之外
拥有reference语义。然而,确保“pointer的寿命”和“其所指向的对象的寿命”一致,却是件棘手的事,特
别是当多个pointer指向同一对象时。例如,为了让多个集合(见第7章)拥有同一对象,你必须把指向该对
象的pointer放进那些集合内,而且当其中一个pointer被销毁时不该出现问题,也就是不该出现所谓的
dangling pointer(空悬指针)或多次删除被指向对象;最后一个pointer被销毁时也不该出现问题(不
该造成资源泄漏【resource leak】)。避免上述问题的一个通常做法是使用smart pointer(智能指针)。
它们被称为smart是因为它们对于上述问题提供了解决方案.举个例子,smart pointer有可能如此smart
以至于能够“知道”它自己是不是“指向某物”的最后一个pointer,并运用这样的知识,在它的确是该对象
的最后一个拥有者而且它被删除时,销毁它所指向的对象.



然而请注意,只提供一个smart pointer class是不够的。Smart pointer可以在很多方面发挥其智能,
并满足不同重点,但你可能会为它的smartness付出代价。注意,对于一个独特的smart pointer,还是
有可能误用pointer或写出容易出错的行为。


自C++11起,C++标准库提供两大类型的smart pointer:

1.Class shared_ptr实现共享式拥有(shared ownership)概念。多个smartpointer可以指向相同对
象,该对象和其相关资源会在“最后一个 reference 被销毁”时被释放。为了在结构较复杂的情境中执
行上述工作,标准库提供了weak_ptr、bad_weak_ptr和enable_shared_from_this等辅助类。

2.Class unique_ptr实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,
保证同一时间内只有一个smart pointer可以指向该对象.你可以移交拥有权.它对于避免资源泄漏
(resource leak)——例如“以new创建对象后因为发生异常而忘记调用delete”——特别有用.



C++98只让C++标准库提供一个smart pointer class:auto_ptr<>,其设计是为了执行现今的
unique_ptr所提供的服务。然而由于当时缺乏语言特性如“针对构造和赋值”的move语义,以及其
他瑕疵,这个class不易被理解且容易出错。因此在TR1引入class shared_ptr,C++11引入class 
unique_ptr之后,class auto_ptr成为C++11中被正式反对(deprecated)的成分,意味着你不该
再使用它,除非你手上还有一些老旧代码需要编译。

所有smart pointer class都被定义于头文件<memory>内。

1.2 稍微总结下

2.Class shared_ptr

2.1 初识shared_ptr

几乎每一个稍有分量的程序都需要“在相同时间的多处地点处理或使用对象”的能力。为此,你必须在程
序的多个地点指向(refer to)同一对象。虽然C++语言提供reference和pointer,还是不够,因为
你往往必须确保当“指向对象”的最末一个reference被删除时该对象本身也被删除,毕竟对象被删除时
析构函数可以要求某些操作,例如释放内存或归还资源等等。所以我们需要“当对象再也不被使用时就
被清理”的语义。Class shared_ptr提供了这样的共享式拥有语义。也就是说,多个shared_ptr可以
共享(或说拥有)同一对象。对象的最末一个拥有者有责任销毁对象,并清理与该对象相关的所有资源。
如果对象以new产生,默认情况下清理工作就由delete完成。但你可以(并且往往必须)定义其他清理
办法。你可以定义你自己的析构策略。举个例子,如果你的对象是个以new[]分配而得的array,你必须
定义你自己的delete[]加以清理。其他例子还包括删除相应资源,如:
handle、
lock、
associated temporary file(相关临时文件)等。


总而言之,shared_ptr的目标就是,在其所指向的对象不再被需要之后(而非之前),自动释放与对象相
关的资源。

2.2 学习使用shared_ptr

 

#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
int main()
{
    
    shared_ptr<string> pNico(new string("nico"),[](string* p){ cout << "delete "<< *p << endl; delete p;});
    shared_ptr<string> pJutta(new string("jutta"));
    
    // shared_ptr<string> pNico  = make_shared<string>("nico");
    // shared_ptr<string> pJutta = make_shared<string>("jutta");
    cout << "line = " <<__LINE__<<", use_count: " <<  pJutta.use_count() <<endl;
    (*pNico)[0] = 'N';
    pJutta->replace(0,1,"J");
    vector<shared_ptr<string>> whoMadeCoffee;
    whoMadeCoffee.push_back(pJutta);  
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);

    for(auto ptr : whoMadeCoffee)
    {
        cout << *ptr << " ";
    }
    cout << endl;
    cout << "line = " <<__LINE__<<", pNico.use_count()             : " <<  pNico.use_count()            <<", pNico = "<< pNico  <<endl;
    cout << "line = " <<__LINE__<<", pJutta.use_count()            : " <<  pJutta.use_count()           <<", pJutta = "<< pJutta << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[0].use_count()  : " <<  whoMadeCoffee[0].use_count() <<", whoMadeCoffee[0] = "<< whoMadeCoffee[0] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[1].use_count()  : " <<  whoMadeCoffee[1].use_count() <<", whoMadeCoffee[1] = "<< whoMadeCoffee[1] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[2].use_count()  : " <<  whoMadeCoffee[2].use_count() <<", whoMadeCoffee[2] = "<< whoMadeCoffee[2] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[3].use_count()  : " <<  whoMadeCoffee[3].use_count() <<", whoMadeCoffee[3] = "<< whoMadeCoffee[3] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[4].use_count()  : " <<  whoMadeCoffee[4].use_count() <<", whoMadeCoffee[4] = "<< whoMadeCoffee[4] << endl;
    cout << endl;
    *pNico = "Nicolai";
    for(auto ptr : whoMadeCoffee)
    {
        cout << *ptr << " ";
    }
    cout << endl;
    cout << "expect pNico == whoMadeCoffee[2] == whoMadeCoffee[4] , pJutta == whoMadeCoffee[0] == whoMadeCoffee[1] == whoMadeCoffee[3]" << endl;
    cout << "line = " <<__LINE__<<", pNico.use_count()             : " <<  pNico.use_count()            <<", pNico = "<< pNico  <<endl;
    cout << "line = " <<__LINE__<<", pJutta.use_count()            : " <<  pJutta.use_count()           <<", pJutta = "<< pJutta << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[0].use_count()  : " <<  whoMadeCoffee[0].use_count() <<", whoMadeCoffee[0] = "<< whoMadeCoffee[0] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[1].use_count()  : " <<  whoMadeCoffee[1].use_count() <<", whoMadeCoffee[1] = "<< whoMadeCoffee[1] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[2].use_count()  : " <<  whoMadeCoffee[2].use_count() <<", whoMadeCoffee[2] = "<< whoMadeCoffee[2] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[3].use_count()  : " <<  whoMadeCoffee[3].use_count() <<", whoMadeCoffee[3] = "<< whoMadeCoffee[3] << endl;
    cout << "line = " <<__LINE__<<", whoMadeCoffee[4].use_count()  : " <<  whoMadeCoffee[4].use_count() <<", whoMadeCoffee[4] = "<< whoMadeCoffee[4] << endl;
    
    pNico = nullptr; 
    cout << "line = " <<__LINE__<<", pNico.use_count()             : " <<  pNico.use_count()            <<", p = "<< pNico  <<endl;
    whoMadeCoffee.resize(2); // call deleter
    cout << "line = " <<__LINE__<<", pNico.use_count()             : " <<  pNico.use_count()            <<", p = "<< pNico  <<endl;
    cout << "line = " <<__LINE__<< endl;

}

Guess you like

Origin blog.csdn.net/Edidaughter/article/details/121182770