C++智能指针详解

C++智能指针是一种面向对象的指针,实现的是一种自主的内存回收机制,即人为开辟内存,系统释放内存;
其实现原理是通过在栈上生成一个对象,去对堆内存进行一个简单管理,对象内部定义有指向堆内存的指针,当对象的生存周期到了之后,系统就会调用析构函数释放对象所占用的资源,那么将指针放入析构函数中进行释放,就形成了手动开辟内存,而系统释放内存的行为,也就是将堆内存的所有权交由栈上的一个对象进行管理

C++11标准引入了unique_ptr、shared_ptr、weak_ptr 三种智能指针,在C++98标准中还存在一种智能指针:auto_ptr ,不过在C++11标准之后就被摈弃了,不建议使用,这里也不做过多赘述

auto_ptr的实现机制就是保证堆内存的所有权唯一,一块堆内存只允许一个智能指针进行管理,当有新智能指针指向该内存块,就回收旧智能指针的所有权,这样实现的结果就是在拷贝构造或赋值的过程中,可能会导致旧智能指针的提前失效,要避免将其放入容器中,算法对容器进行操作时,很难避免STL内部对容器进行复制传递操作,易导致容器内部的一些元素被置空

unique_ptr

实现机制: 禁止权限转移,所有权唯一,不允许多智能指针指向同一堆内存;必须使用直接初始化操作直接进行赋值(拷贝构造与赋值运算符重载函数皆存放在私有域下)

unique_ptr<int> p (new int);

//放弃对指针的控制权并返回内置指针,原智能指针置空,但并不会释放其指向的内存
p.release();

//释放所指向的对象
p.reset();

//release常用于初始化另一个智能指针,若未用另一智能指针保存返回的指针,则需要程序进行资源释放
q.reset(P.release());//q指向p,并释放原先指向的内存
p.reset(m);//m必须是内置指针

缺点:

int *p = new int();
unique_ptr<int> p1(p);
unique_ptr<int> p2(p);
unique_ptr<int> p3(p);
//使用点又让多智能指针指向了同一堆内存,重复释放出现问题

shared_ptr(使用最多)

实现机制: 带引用计数的智能指针(强智能指针),允许多智能指针指向同一堆内存,最后一个销毁对象释放所指向的堆内存

构造

shared_ptr<int> p1(new int(10));//推荐使用

shared_ptr<int> p2 = p1;//拷贝构造,引用计数+1

//返回一个shared_ptr类型的智能指针并动态分配一个指定类型的对象,并做初始化,该函数也是最安全的分配及使用动态内存的方式
shared_ptr<int> p3 = make_shared<int>(10);

//智能指针离开了其作用域,引用计数减少

//使用同一内置指针初始化两个shared_ptr,引用计数可能会出错
int *temp = new int(10);
{
    shared_ptr<int> p4(temp);
    {
        shared_ptr<int> p5(temp);
    }
}//内存重复释放,崩溃

//因此第一个智能指针直接构造初始化,其他智能指针只能用其他智能指针初始化

操作:

//返回与p1共享对象的智能指针的数量
p1.use_count()

//若p1是唯一指向该对象的智能指针,reset会释放该对象
p1.reset()

//返回p1所保存的指针,返回的是内置指针,若智能指针释放了对象,则该指针所指向的对象也消失了
p1.get()

//不要用get()为另一个智能指针初始化或赋值,如:
shared_ptr<int> ptr(p1.get());
//ptr与p1的各自引用计数都为1,互相独立,可能会造成提前释放对象

//不能将一个内置指针隐式转换为智能指针,因此必须使用直接初始化方式
shared_ptr<int> p2(new int(30));
//将一个shared_ptr的所有权转移给另一个shared_ptr,并将原先shared_ptr置空,p2 == NULL
shared_ptr<int> p3 = move(p2);

缺点:
shared_ptr对于引用计数处理的关联性太强,只要存在指针指向,引用计数就+1,相互引用问题可能会导致内存泄露(两个类同时包含指向另一个类的shared_ptr智能指针)

weak_ptr

实现机制: 主要为解决强智能指针相互引用的问题而产生(弱智能指针),不可单独使用,不添加引用计数,结合shared_ptr使用,weak_ptr指向由shared_ptr所管理的对象

p.reset();//置空p

p.use_count();//与p共享的shared_ptr数量

p.expired();//若use_count==0,返回True,反之False

p.lock();//若expired为True,返回一个空shared_ptr,否则返回一个指向p的对象的shared_ptr

//weak_ptr不会改变引用计数,weak_ptr所指向对象可能被释放掉,因此不能直接使用weak_ptr访问对象,必须调用lock(),检查对象是否存在并返回一个shared_ptr进行操作
//一般放入对象类中作为成员变量在相互引用时使用

如果不存在循环引用,就不需要使用weak_ptr

shared_ptr相互引用问题

class B;
class A
{
public:
    A(){
        
    }
    ~A(){
        
    }
    void set(shared_ptr<A> a)
    {
        pb = a;
    }
    
private:
    shared_ptr<B> pb;
};

class B
{
public:
    B(){
        
    }
    ~B(){
        
    }
    void set(shared_ptr<B> b)
    {
        pa = b;
    }
    
private:
    shared_ptr<A> pa;
};
int main()
{
    A *a = new A();
    B *b = new B();
    
    {
    	shared_ptr<A> ma(a);
    	shared_ptr<B> mb(b);
    
   		a->set(mb);
    	b->set(ma);
        
    	cout << ma.use_count() << endl;//2
    	cout << mb.use_count() << endl;//2
    }
    
    return 0;
}
/*
退出作用域前,两堆内存的引用计数都是2
退出作用域后,ma,mb两智能指针离开作用域,引用计数减一,两堆内存的引用计数都是1
类对象内部还存在智能指针指向堆内存,对象无法析构,内存无法释放,内存泄漏
*/

//解决方法:两强智能指针相互引用时,将其中任意一方类中包含的shared_ptr改为weak_ptr即可
/*
退出作用域前,两堆内存的引用计数分别是2,1
退出作用域后,ma,mb两智能指针离开作用域,引用计数减一,两堆内存的引用计数分别是1,0,
引用计数为0的类对象内部包含的智能指针应是shared_ptr,释放对象内存空间,调用析构函数
shared_ptr智能指针离开类作用域,指向内存的引用计数减一,另一堆内存的引用计数也变为0,释放内存
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值