C++智能指针

四大智能指针与RAII

https://blog.csdn.net/Ferlan/article/details/86513679

RAII即用对象管理资源,在对象的生命周期内,该资源一直有效,当对象被析构时,相应的资源被释放。

而智能指针就是基于这个原理,new关键字开辟的内存往往需要手动delete,一旦程序员忘记delete,则会造成内存泄漏,所以引用智能指针,通过智能指针对象来管理该指针,即管理该内存资源。当智能指针对象析构时,不容的智能指针对象采用不同的释放资源策略。

auto_ptr/unique_ptr/shared_ptr/weak_ptr

  1. unique_ptr:一种智能指针,其拥有其所指向的对象的独有权
    • 不能拷贝和赋值到其他unique_ptr中
    • 但是可以进行移动操作,用于接管其他unique_ptr的对象,从而将内存资源的所有权进行转移
  2. auto_ptr 被淘汰
  3. shared_ptr 一个标准的共享所有权的智能指针,即该指针所指向的对象可以被其他智能指针所共享
    • 允许多个指针指向同一个对象
    • 通过引用计数的方式实现了对所管理的对象的分享,只有当管理该对象的所有智能指针全部析构时,才会释放该对象资源
    • shared_ptr引入引用计数会加大空间开销
  4. weak_ptr,是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,将该指针绑定到一个shared_ptr上不会改变引用计数,一旦shared_ptr所指向的对象被释放,虽然weak_ptr还是指向,但是对象还是被释放了
    • 一种对对象的弱引用,不会增加对象的引用计数
    • 可以和shared_ptr之间互相转换
    • 主要是用于解决shared_ptr的循环引用的问题

shared_ptr所造成的循环引用

当两个对象使用shared_ptr来管理,同时互相使用一个shared_ptr成员变量指向对方,则会造成循环引用

class B; // 前置声明
class A {
public:
    shared_ptr<B> ptr;
};

class B {
public:
    shared_ptr<A> ptr;
};

int main()
{
    while(true) {
        shared_ptr<A> pa(new A());
        shared_ptr<B> pb(new B());
        pa -> ptr = pb;
        pb -> ptr = pa;
    }
    return 0;
}

解决这种问题的最好方法是才对象内部采用weak_ptr来指向对方(把一个改成weak_ptr即可)

shared_ptr是线程安全的,标准库对其的实现,使用原子操作来操作引用计数

new/delete malloc/free

malloc/free 是标准库函数,其作用是分配空间和释放空间,但是其分配空间不会在空间上构造相应的对象,即是未初始化的,未构造的,不可访问的空间,而free只是释放空间,但是并不会调用空间中存储的对象的析构函数。

new/delete是C++的操作符

new pointer 的实际步骤是

  1. 调用operator new /operator new[]重载运算符,分配原始内存空间
  2. 调用对象的默认构造函数,在所分配的内存上构造对象

delete的实际步骤是

  1. 对内存中保存的对象,显示调用析构函数,析构相应的对象
  2. 调用operator delete/operator delete[] 重载运算符,释放所分配的空间

这里就要注意一个问题,即delete p和delete [] p的区别,我们常规的理解认为前者用于释放单个对象,后者用于释放一组对象,其实无论是前者还是后者都会释放掉所有申请的堆内存,但是前者只会调用第一个元素的析构函数,而后者会调用数组中所有元素的析构函数,故对于内置类型,delete和delete[]没有区别,但是自定义类型则有区别

标准库允许用户定义重载的operator new/operator new[] operator delete/operator delete[]用于定义自己的内存分配和释放操作

标准库中提供了几种重载的new 和 delete函数

void* operator new(size_t);
void* operator new[](size_t);
void* operator delete(void*) noexcept;
void* operator delete[](void*) noexcept;
......

上述的函数可以被重载但是,operator new(size_t , void*)是不能被重载的,因为该函数有特殊的用处

实际而言operator new/operator free 实际上就是调用的malloc和free函数,其只能分配原始内存空间,或者只能删除析构后的空间,那么谁来负责在内存上构造和析构元素呢?

定位new表达式

new(pointer)type;
new(pointer)type (initializers);
new(pointer)type [size];
new(pointer)type [size] {};

定位表达式主要用于调用构造函数在内存中构造元素,该表达式会先调用operator new(size_t , void*),该函数不干任何事情,只是返回实参,然后由new表达式负责初始化

显示析构,即通过对象的指针显示调用析构函数

p->~A();

关于shared_ptr的线程安全问题

  1. 首先shared_ptr其内部实现是线程安全的,即其内部引用计数的实现是线程安全的,在标准库中引用计数是原子操作,所以是线程安全的
  2. 但是对于shared_ptr所指向的对象而言,其并不是线程安全的,即可以同时读取一个shared_ptr,但是不能同时读写/同时写一个shared_ptr,这样并不是线程安全的
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值