C++ STL Smart Pointer 智能指针(一)shared_ptr和weak_ptr

C++标准库提供了两大类型的smart pointer

  一、class shared_ptr。共享式拥有。多个shared_ptr可以指向同一个对象,该对象和起相关资源会在最后一个指针被销毁时释放。

  二、class unique_ptr。独占式拥有。同一时间只有一个smart pointer可以指向该对象。对于避免资源泄露特别有用。

1.Class shared_ptr

1.1 使用shared_ptr

  可以像使用其他指针一样使用shared_ptr。可以赋值、拷贝、比较,也可以使用*和->操作符。

#include <iostream>
#include <string>
#include <memory>
#include <vector>

using namespace std;

int main(){
    shared_ptr<string> pNico(new string("nico"));
    shared_ptr<string> pJutta(new string("jutta"));
    cout<<*pNico<<endl;
    cout<<*pJutta<<endl;
    (*pNico)[0]='N';
    pJutta->replace(0,1,"J");
    cout<<*pNico<<endl;
    cout<<*pJutta<<endl;
    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);
    cout<<"vector whoMadeCoffee:"<<endl;
    for(auto ptr:whoMadeCoffee){
        cout<<*ptr<<" ";
    }
    cout<<endl;
    *pNico="Nicolai";
    cout<<"vector whoMadeCoffee:"<<endl;
    for(auto ptr:whoMadeCoffee){
        cout<<*ptr<<" ";
    }
    cout<<endl;
    cout<<"use_count:"<<whoMadeCoffee[0].use_count()<<endl;
    whoMadeCoffee.clear();
    cout<<"use_count:"<<whoMadeCoffee[0].use_count()<<endl;
    return 0;
}

输出:

nico
jutta
Nico
Jutta
vector whoMadeCoffee:
Jutta Jutta nico Jutta nico
vector whoMadeCoffee:
Jutta Jutta Nicolai Jutta Nicolai
use_count:4
use_count:1

1.2 析构策略

  1 我们可以声明属于自己的deleter。

  2 对于Array 我们可以用delete[]

#include <iostream>
#include <string>
#include <memory>
#include <vector>

using namespace std;

int main(){
    //定义一个Deleter
    shared_ptr<string> pNico(new string("nico"),[](string* p){
        cout<<"delete "<<*p<<endl;
        delete p;
    });
    shared_ptr<string> pJutta(new string("jutta"));
    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);

    //delete all pNico
    pNico=nullptr;
    whoMadeCoffee.resize(2);

    cout<<"vector whoMadeCoffee:"<<endl;
    for(auto ptr:whoMadeCoffee){
        cout<<*ptr<<" ";
    }
    cout<<endl;
    //对付Array
    shared_ptr<int> p(new int[10],[](int* p){
        delete[] p;
    });
    shared_ptr<int> p2(new int[10],default_delete<int[]>());

    unique_ptr<int[]> p3(new int[10]);
    unique_ptr<int,void(*)(int*)> p4(new int[10],[](int* p){
        delete[] p;
    });
    return 0;
}

  运行结果:

delete nico
vector whoMadeCoffee:
jutta jutta

  3 其他析构策略

  你可以制定属于自己的析构策略。比如当最后一个ptr被析构时,删除文件,你可以:

#include <string>
#include <memory>
#include <fstream>
#include <cstdio>

using namespace std;

class FileDeleter
{
public:
    FileDeleter(const string& fn):filename(fn){

    }
    void operator ()(ofstream* fp){
        fp->close();                //close file
        remove(filename.c_str());   //delete file
        cout<<"remove file"<<endl;
    }
private:
    string filename;
};
int main(){ shared_ptr<ofstream> fp(new ofstream("tmpfile.txt"),FileDeleter("tmpfile.txt")); fp->write("aaa\n",4); fp->write("aaa\n",4); fp=nullptr; }

  

2.Class weak_ptr

   weak_ptr允许你“共享但不拥有”对象,这个class会建立起一个shared_pointer。一旦最末一个拥有该对象的shared pointer失去了拥有权,任何weak pointer 都会自动成空。因此除了default和copy构造函数之外,weak_ptr只提供“接受一个shared_ptr”的构造函数。

  你不能使用*和->访问weak_ptr所指的对象。而是必须另外建立一个shared pointer。

person.h

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class Person : public enable_shared_from_this<Person>
{
public:
    Person(const string& n,shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr);
    ~Person();
    void setParentsAndTheirKids(shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr);

    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<shared_ptr<Person>> kids;

};
shared_ptr<Person> initFamily(const string& name);

 

person.cpp

#include "person.h"

Person::Person(const string& n,shared_ptr<Person> m,shared_ptr<Person> f):
    name(n),mother(m),father(f)
{

}
Person::~Person(){
    cout<<"delete "<<name<<endl;
}
void Person::setParentsAndTheirKids(shared_ptr<Person> m, shared_ptr<Person> f){
    mother=m;
    father=f;
    if(m!=nullptr){
        m->kids.push_back(shared_from_this());
    }
    if(f!=nullptr){
        f->kids.push_back(shared_from_this());
    }
}
shared_ptr<Person> initFamily(const string& name){
    shared_ptr<Person> mom(new Person(name+"'s mom"));
    shared_ptr<Person> dad(new Person(name+"'s dad"));
    shared_ptr<Person> kid(new Person(name,mom,dad));
    mom->kids.push_back(kid);
    dad->kids.push_back(kid);
    return kid;
}

 

main.cpp


int
main(){ shared_ptr<Person> p=initFamily("nico"); cout<<"nico's family exists"<<endl; cout<<"- nico is shared "<<p.use_count()<<" times"<<endl; //使用weak_ptr时,必须请问改变被指对象的访问方式,在式子内加上lock() cout<<"- name of 1st kid of nico's mom: "<<p->mother->kids[0].lock()->name<<endl; p=initFamily("jim"); cout<<"jim's family exists"<<endl; }

输出:

nico's family exists
- nico is shared 3 times
- name of 1st kid of nico's mom: nico
jim's family exists

 

将person.h改为:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class Person : public enable_shared_from_this<Person>
{
public:
    Person(const string& n,shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr);
    ~Person();
    void setParentsAndTheirKids(shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr);

    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<weak_ptr<Person>> kids;

};
shared_ptr<Person> initFamily(const string& name);

 

使用weak_ptr时,我们必须改变一下被指对象的访问方式,必须在式子内加入lock()。

这会导致新产生一个得自于kids容器内含之weak_ptr的shared_ptr。如果无法进行这样的改动,lock()会产生一个empty的shared_ptr。

main.cpp改为

int main(){
    shared_ptr<Person> p=initFamily("nico");
    cout<<"nico's family exists"<<endl;
    cout<<"- nico is shared "<<p.use_count()<<" times"<<endl;
    //使用weak_ptr时,必须请问改变被指对象的访问方式,在式子内加上lock()
    cout<<"- name of 1st kid of nico's mom: "<<p->mother->kids[0].lock()->name<<endl;
    p=initFamily("jim");
    cout<<"jim's family exists"<<endl;
    return 0;
}

输出:

nico's family exists
- nico is shared 1 times
- name of 1st kid of nico's mom: nico
delete nico
delete nico's dad
delete nico's mom
jim's family exists
delete jim
delete jim's dad
delete jim's mom

   weak_ptr是shared_ptr的帮手,用来共享对象但不拥有对象。使用use_count()返回的是对象被shared_ptr拥有的次数,至于weak_ptr对它的共享次数是不计的。而且weak_ptr可以为空。

  一般而言,shared_ptr并非线程安全的。因此为避免data race造成的不明确行为,当你在多个线程中以shared_pointer指向同一个对象,你必须使用诸如mutex或lock等技术。

posted @ 2020-05-27 23:08  啸傲风月  阅读(392)  评论(0编辑  收藏  举报