• c++——智能指针学习(shared_ptr和weak_ptr)


     先看一个例子:Stark和Targaryen家族你中有我,我中有你。我们设计以下类企图避免内存泄漏,使得析构函数都能调用到:

    #include<iostream>
    #include<memory>
    using namespace std;
    
    class Stark;
    class Targaryen;
    
    class Stark
    {
    	private:
    	  Targaryen *targaryen;
    
    	public:
    		void prin(){cout<<"stark love targaryen"<<endl;}
    		~Stark()
    		{
    			delete targaryen;
    			cout<<"~Stark"<<endl;
    		}
    };
    
    class Targaryen
    {
      private:
    	Stark *stark;
    
      public:
    	void prin() { cout << "targaryen love stark" << endl; }
    	~Targaryen()
    	{
    		delete stark;
    		cout << "~Targaryen" << endl;
    	}
    };
    
    int main()
    {
    	Targaryen *tar    = new Targaryen;
    	Stark     *stark  = new Stark;
    
    	delete stark;
    
    	system("pause");
    	return 0;
    } 
    

     打印结果:

    正常来说,我们要求的结果是两个对象都要析构掉,但是我们可以debug执行看到,并没有全部析构,显然不是我们的需求!

    那么换一种智能指针的写法,看看结果怎么样:

    class Stark;
    class Targaryen;
    
    class Stark
    {
    	private:
    	  //Targaryen *targaryen;
    	shared_ptr<Targaryen> share_tar;
    	public:
    		void prin(){cout<<"stark love targaryen"<<endl;}
    		void setTargaryen(shared_ptr<Targaryen> tar)
    		{
    			this->share_tar = tar;
    		}
    		~Stark()
    		{
    			//delete targaryen;
    			cout<<"~Stark"<<endl;
    		}
    };
    
    class Targaryen
    {
      private:
    	//Stark *stark;
    	shared_ptr<Stark> share_stark;
    	//weak_ptr<Stark> weak_stark;
    
      public:
    	void prin() { cout << "targaryen love stark" << endl; }
    	void setStark(shared_ptr<Stark> stark)
    	{
    		this->share_stark = stark;
    	}
    	~Targaryen()
    	{
    		//delete stark;
    		cout << "~Targaryen" << endl;
    	}
    };
    
    int main()
    {
    	weak_ptr<Stark> wpstark;
    	weak_ptr<Targaryen> wptar;
    
    	//Targaryen *tar    = new Targaryen;
    	//Stark     *stark  = new Stark;
    	{
    	shared_ptr<Targaryen> tar(new Targaryen);
    	shared_ptr<Stark> stark(new Stark);
    	tar->prin();
    	stark->prin();
    	tar->setStark(stark);
    	stark->setTargaryen(tar);
    	wpstark = stark;
    	wptar = tar;
    	cout << tar.use_count() << endl;
    	cout << stark.use_count() << endl;
    	}
    
    	cout << wpstark.use_count() << endl;
    	cout << wptar.use_count() << endl;
    
    	//delete stark;
    
    	system("pause");
    	return 0;
    } 
    

     我们希望在main中第一对{}号结束的时候,打印析构函数,但是并没有

    那我们再换一种写法:

    class Stark;
    class Targaryen;
    
    class Stark
    {
    	private:
    	  //Targaryen *targaryen;
    	shared_ptr<Targaryen> share_tar;
    	public:
    		void prin(){cout<<"stark love targaryen"<<endl;}
    		void setTargaryen(shared_ptr<Targaryen> tar)
    		{
    			this->share_tar = tar;
    		}
    		~Stark()
    		{
    			//delete targaryen;
    			cout<<"~Stark"<<endl;
    		}
    };
    
    class Targaryen
    {
      private:
    	//Stark *stark;
    	//shared_ptr<Stark> share_stark; ////这里变了~~~~~~~~~~~~
    	weak_ptr<Stark> weak_stark;
    
      public:
    	void prin() { cout << "targaryen love stark" << endl; }
    	void setStark(shared_ptr<Stark> stark)
    	{
    		this->weak_stark = stark;
    	}
    	~Targaryen()
    	{
    		//delete stark;
    		cout << "~Targaryen" << endl;
    	}
    };
    

     这次结果还是令人满意的。

    那么问题来了,为什么要这么做呢?为什么要用weak_ptr取代shared_ptr呢?

    我们看weak_ptr的官方定义:

    std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr

    std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

    std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

    这种就是一种环状的情况。

    另外,还有一点要注意:

     1 int main()
     2 {
     3     {
     4         int *a = new int;
     5         std::shared_ptr<int> p1(a);
     6         std::shared_ptr<int> p2(a);
     7     }
     8     system("pause");
     9     return 0;
    10 }

    这样写会报错。因为,析构的时候,指针a会被delete两次。因此,为了避免这种情况的发生,我们尽可能不适用new来初始化shared_ptr。而是用make_shared;

     1 class Mars
     2 {
     3   public:
     4     Mars ()
     5     {
     6         cout << this << ": Mars" << endl;
     7     }
     8     ~Mars()
     9     {
    10         cout << this << ": ~Mars" << endl;
    11     }
    12 };
    13 int main()
    14 {
    15     {
    16         Mars pMars;
    17         shared_ptr<Mars> p1 = make_shared<Mars >(pMars);
    18         shared_ptr<Mars> p2 = make_shared<Mars >(pMars);
    19     }
    20     system("pause");
    21     return 0;
    22 }

    这玩意儿太复杂了~只是清楚大概是干什么的。但是还不会用……以及什么时候用,关键是我们这公司平时也不用,这就尴尬了。

    用shared_ptr,不用new
    使用weak_ptr来打破循环引用
    用make_shared来生成shared_ptr
    用enable_shared_from_this来使一个类能获取自身的shared_ptr

    结束!以后有时间再慢慢研究。

    大部分都是抄的:

    https://zh.cppreference.com/w/cpp/memory/shared_ptr

    https://www.cnblogs.com/wxquare/p/4759020.html

    https://blog.csdn.net/worldwindjp/article/details/18843087

    https://heleifz.github.io/14696398760857.html

  • 相关阅读:
    json不支持中文写入的问题解决
    include(thinkphp常用内置标签)
    php如何设置编码格式
    模板替换
    获取网页内容之后图片不显示
    远程调用数据文件内容
    文件上传
    getError自动验证
    php文档编码设置
    获取网页内容时的乱码问题
  • 原文地址:https://www.cnblogs.com/whutao/p/10595057.html
Copyright © 2020-2023  润新知