谈谈C++智能指针

目录引导:

            智能指针是什么

            智能指针的使用场景

            智能指针的使用方法

一. 智能指针是什么       ‘

        智能指针是C++11提供的一种类,采用RAII的高级编程设计思想,可以自动管理类的对象的生命周期;感兴趣的朋友可以阅读《Modern C++》这本书,该书作者在书中有具体的讲解。在C++11中有三个智能指针:shared_ptr,unique_ptr, weak_ptr。unique_ptr独占对象的所有权,由于没有引用计数,因此性能较好。 shared_ptr共享对象的所有权,但性能略差。 weak_ptr配合shared_ptr,解决循环引用的问题。

二. 智能指针的使用场景

      智能指针主要解决以下问题: 1. 内存泄漏:内存手动释放,使用智能指针可以自动释放 malloc free; new delete 2. 共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题。

三. 智能指针的使用方法

1,shared_ptr的使用方法

(1)shared_ptr的内存模型:

std::shared_ptr<T>在栈上创建,它的内存模型有两个部分:1.Ptr to T; 2.Ptr to Control Block。一个智能指向指针对象的指针,该对象在堆区;一个指向控制块的指针。控制块中包含一个 引用计数(reference count), 一个弱计数(weak count)和其它一些数据。示意图如下:

 当一个share_ptr<T>对象离开作用域,或者调用reset()函数,则相应的Reference Count减1,当Refernce Count减到0的时候,就会去释放堆上的指针,该share_ptr<T>指针被析构。

(2) shared_ptr的基本用法和常用函数:

    s.get():返回shared_ptr中保存的裸指针;

    s.reset(…):重置shared_ptr;

reset( )不带参数时,若智能指针s是唯一指向该对象的指针,则释放,并置空。若智能指针P不是唯 一指向该对象的指针,则引用计数减少1,同时将P置空。 reset( )带参数时,若智能指针s是唯一指向对象的指针,则释放并指向新的对象。若P不是唯一的指 针,则只减少引用计数,并指向新的对象。如:

 auto s = make_shared(100); 
 s.reset(new int(200));

s.use_count() :返回shared_ptr的强引用计数;

s.unique() :若use_count()为1,返回true,否则返回false。

初始化shared_ptr<T>p的方法有下面几种

shared_ptr<int>sp1(new int(10));
shared_ptr<int>sp2=sp1;
shared_ptr<int>sp3;
sp3.reset(new int(12);
shared_ptr<int>sp4=make_shared<int>(10);//这种方法更高效,我们应该采用这种方法

错误的初始化方式:

不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的:

std::shared_ptr<int> p = new int(1);

不要用一个原始指针初始化多个shared_ptr

int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误

不要在函数实参中创建shared_ptr

function(shared_ptr<int>(new int), g()); //有缺陷

因为C++的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的,一般是从右到左,但也可能从左到右,所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建, 则int内存泄漏了,正确的写法应该是先创建智能指针,代码如下:

shared_ptr<int> p(new int);
function(p, g());

通过shared_from_this()返回this指针

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
shared_ptr<A> GetSelf()
{
return shared_ptr<A>(this); // 不要这么做
}
~A()
{
cout << "Destructor A" << endl;
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->GetSelf();
return 0;
}

运行后调用了两次析构函数。 在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系 的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

参考一下的代码,你会更加体会到shared_ptr<T>这个智能指针:

#include<iostream>
#include<memory>

using namespace std;

int main(void) {
	shared_ptr<int>sp1(new int(100));
	cout << sp1.use_count() << endl;//1;
	shared_ptr<int>sp2 = sp1;
	cout << sp1.use_count() << endl;//2;
	sp2.reset(new int(10));
	cout << sp2.use_count() << endl;//1;
	cout << sp1.use_count() << endl;//1

	sp1.reset();
	cout << sp2.use_count() << endl;//1;
	cout << sp1.use_count() << endl;//0
}

避免循环引用, 循环引用会导致内存泄漏,比如:

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;

class A {
public:
	std::shared_ptr<A> aptr;
	~A() {
		cout << "A is deleted" << endl;
	}
};
class B {
public:
	std::shared_ptr<B> bptr;
	~B() {
		cout << "B is deleted" << endl;
	}
};

int main()
{
	{
		std::shared_ptr<A> ap(new A);
		std::shared_ptr<B> bp(new B);
		ap->aptr = ap;
		bp->bptr = bp;
		cout << ap.use_count() << endl;//2

	}
	cout << "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构
	return 0;
}

无法析构掉A,B这两个堆上的类的的对象。循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导 致两个指针都不会被析构,产生内存泄漏。 解决的办法是把A和B成员变量都改为weak_ptr。

(3)unique_ptr独占的智能指针

1. unique_ptr是一个独占型的智能指针,不能将其赋值给另一个unique_ptr ;  unique_ptr可以指向一个数组 ; unique_ptr需要确定删除器的类型

除了unique_ptr的独占性, unique_ptr和shared_ptr还有一些区别,比如: unique_ptr可以指向一个数组,代码如下所示

std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9;
std::shared_ptr<int []> ptr2(new int[10]); // 这个是不合法的

 unique_ptr指定删除器和shared_ptr有区别

std::shared_ptr<int> ptr3(new int(1), [](int *p){delete p;}); // 正确
std::unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // 错误

而要这样写:

  std::unique_ptr<int, void(*)(int*)> ptr5(new int(1), [](int *p){delete  p;}); // 正确

关于shared_ptr和unique_ptr的使用场景是要根据实际应用需求来选择。 如果希望只有一个智能指针管理资源或者管理数组就用unique_ptr,如果希望多个智能指针管理同一个 资源就用shared_ptr。

(3)weak_ptr弱引用的智能指针

 概念:

     share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互 使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。 weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。 weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从 一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

基本用法

1. 通过use_count()方法获取当前观察资源的引用计数,如下所示:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
cout << wp.use_count() << endl; //结果输出1

2. 通过expired()方法判断所观察资源是否已经释放,如下所示:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
if(wp.expired())
cout << "weak_ptr无效,资源已释放";
else
cout << "weak_ptr有效";

3. 通过lock方法获取监视的shared_ptr,如下所示:

#include<iostream>
#include<memory>

using namespace std;

weak_ptr<int> gw;

        void f()
        {
            auto spt = gw.lock();
            if (gw.expired()) {
                cout << "gw无效,资源已释放";
            }
            else {
                cout << "gw有效, *spt = " << *spt << endl;
            }
        }
        
        int main()
        {
            {
                auto sp = std::make_shared<int>(42);
                gw = sp;
                f();
            }
            f();
            return 0;
        }

输出结果: 

 

 

  

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
智能指针C++中的一种特殊类型的指针,它能够自动管理内存的释放,避免了手动释放内存的繁琐和容易出错的问题。C++11引入了几种智能指针,包括带引用计数的智能指针和不带引用计数的智能指针,如auto_ptr, scoped_ptr, unique_ptr, shared_ptr, weak_ptr等。\[1\] 带引用计数的智能指针(shared_ptr)使用引用计数来跟踪指针的引用次数,当引用计数为0时,自动释放内存。这种智能指针适用于多个指针共享同一块内存的情况,可以避免内存泄漏和悬挂指针的问题。 不带引用计数的智能指针(unique_ptr)则采用了独占所有权的方式,确保每个指针只能指向一个对象。当指针超出作用域或被显式释放时,内存会被自动释放。这种智能指针适用于需要独占资源的情况,如动态分配的内存、文件句柄等。 自己实现智能指针可以通过重载指针操作符和使用引用计数等技术来实现。但需要注意的是,自己实现智能指针需要考虑线程安全性和异常安全性等问题,确保指针的正确释放和资源的正确管理。 然而,需要注意的是,智能指针并不能解决所有的问题。例如,当存在循环引用时,带引用计数的智能指针(shared_ptr)可能导致内存泄漏。此外,使用智能指针时也需要注意避免裸指针和智能指针混用的情况,以免出现悬挂指针或重复释放内存的问题。\[2\]\[3\] 总之,深入掌握C++智能指针需要了解各种智能指针的原理、用法和适用场景,并注意使用智能指针的注意事项,以确保代码的正确性和安全性。 #### 引用[.reference_title] - *1* *2* [【C++】深入掌握智能指针](https://blog.csdn.net/weixin_45853856/article/details/121184992)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [722-深入掌握C++智能指针](https://blog.csdn.net/LINZEYU666/article/details/120982178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值