RAII 与智能指针

1.什么是 RAII

1.概念
Resource Acquisition Is Initialization 资源获取即初始化。一般分为 3 步,当我们在main函数中声明一个局部对象的时候,会自动调用构造函数进行对象的初始化,当整个main函数执行完成后,自动调用析构函数来销毁对象,整个过程无需人工介入,由操作系统自动完成;

2.如何使用 raii

智能指针(std::shared_ptr和std::unique_ptr)即RAII最具代表的实现,使用智能指针,可以实现自动的内存管理,再也不需要担心忘记delete造成的内存泄漏。

还有就是锁,有可能出现初始化锁但是没有释放锁的情况

2.智能指针

智能指针源码实现

导入头文件

#include <memory>

2.1 内存泄露

一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从中分配的,大小任意的(内存块的大小可以在程序运行期决定)内存块,使用完后必须显式释放的内存。应用程序般使用malloc,、realloc、 new等函数从堆中分配到块内存,

避免内存泄露的几种方式

  • 计数法:使用new或者malloc时,让该数+1,delete或free时,该数-1,程序执行完打印这个计数,如果不为0则表示存在内存泄露
  • 一定要将基类的析构函数声明为虚函数
~A() = default;
  • 对象数组的释放一定要用delete []
  • 有new就有delete,有malloc就有free,保证它们一定成对出现

检测工具

  • Linux下可以使用Valgrind工具

    https://blog.csdn.net/qq_20553613/article/details/106503929

会显示有几个指针是 free 的有几个指针是 alloc 的,显示是在哪一行出错的,同样 Valgrind 还能检测其他的内存问题:

valgrind --tool=memcheck --leak-check=full ./a

使用空指针

使用野指针:指向了一个根本不存在或者已经被释放的指针

内存空间访问越界

内存空间未释放

内存空间重复释放

内存空间申请和释放不匹配

  • Windows下可以使用CRT库

3.auto_ptr

定义:auto_ptr的实现原理其实就是RAII,在构造的时候获取资源,在析构的时候释放资源,并进行相关指针操作的重载

抛弃原因:因为有拷贝构造和赋值的情况下原有的对象会被释放

https://blog.csdn.net/Dasis/article/details/121663794

(1)赋值

当我们对智能指针进行赋值时,如 ptest2 = ptest,ptest2会接管ptest原来的内存管理权,ptest会变为空指针,如果ptest2原来不为空,则它会释放原来的资源

(2)数组传递值,本质也是移动拷贝

void foo_ary()
{
    std::vector<std::auto_ptr<int>> Ary;
    std::auto_ptr<int> p(new int(3));
    //push_back时不允许如此构造对象
    Ary.push_back(p);

    printf("%d\r\n", *p);
}

4.shared_ptr

多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。

(1)创建

std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p5 = std::make_shared<int>(10);
//调用拷贝构造函数
std::shared_ptr<int> p4(p3);//或者 std::shared_ptr<int> p4 = p3;
//调用移动构造函数
std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);

(2) 源码

//
// Created by xuboluo on 2022/9/5.
// shared_prt_demo

#include <iostream>
template <typename T>
class my_shared{
  int count_;
  T* ptr_;
  public:
  // 创建构造函数
  // 1. 默认构造函数
  my_shared():count_(0),ptr_(nullptr){}
  // 2. 传入的是对象
  my_shared(T* ptr):ptr_(ptr),count_(1){}
  // 3.传入的是智能指针
  my_shared(const my_shared<T>& other):count_((other.count_+1)),ptr_(other.ptr_){}

  // 析构函数
  ~my_shared(){
    count_--;
    if(ptr_&&count_==0){
      delete ptr_;
    }
  }
  // operator = 的方法
  my_shared* operator=(const my_shared<T>& other){
    if(this==&other) return this;
    other.count_++;
    // 将 prt2 指向的对象给 ptr1 ,并将自己原先的进行释放
    if(this->ptr_&&0==--this->count_){
      delete ptr_;
    }
    // 释放完之后再向 ptr 赋值
    this->ptr_ = other.ptr_;
    this->count_ = other.count_;
    return this;
  }
  // use_count
  int use_count(){
    return count_;
  }
};
int main(){
  int* p = new int(3);
  my_shared<int> ptr(new int(3)); // 传入一个对象的指针
  my_shared<int> ptr2(ptr); // 传入一个智能指针
  my_shared<int> ptr3 = ptr2;
  std::cout<<ptr3.use_count();
}

(3)Shared_ptr 的循环引用
如果相互引用 shared_ptr 会造成程序死锁,最终谁也不释放资源
Demo

5.weak_ptr

解决 shared_ptr 相互引⽤时,两个指针的引⽤计数永远不会下降为0,从⽽导致死锁问题。⽽ weak_ptr 是对对象的⼀种弱引⽤,可以绑定到 shared_ptr ,但不会增加对象的引⽤计数。

如何判断 weak_ptr 失效:1.expired():检查被引用的对象是否已删除。2.lock()会返回shared指针,判断该指针是否为空。3.use_count()也可以得到shared引用的个数,但速度较慢

(1)创建

std::shared_ptr<int> sp (new int);
std::weak_ptr<int> wp3 (sp);

(2)如何使用

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

int main()
{
  std::shared_ptr<int> sp1(new int(10));
  std::shared_ptr<int> sp2(sp1);
  std::weak_ptr<int> wp(sp2);
  //输出和 wp 同指向的 shared_ptr 类型指针的数量
  cout << wp.use_count() << endl; // 2
  //释放 sp2
  sp2.reset();
  cout << wp.use_count() << endl; // 1 
  //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
  cout << *(wp.lock()) << endl; // 10
  return 0;
}

6.unique_ptr

是作用域指针,当超出作用域时就会被销毁然后调用 delete。同⼀时间只能有⼀个智能指针可以指向该对象,每个 unique_ptr 指针指向的堆内存空间的引用计数,都只能为 1,一旦该 unique_ptr 指针放弃对所指堆内存空间的所有权,则该空间会被立即释放回收,而且 unique_ptr 的拷贝构造函数和 operator= 都被 delete 了

(1)创建

std::unique_ptr<int> p4(new int);
std::unique_ptr<int> p5(p4);//错误,堆内存不共享
std::unique_ptr<int> p5(std::move(p4));//正确,调用移动构造函数

(2)如何使用

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

int main()
{
    std::unique_ptr<int> p5(new int);
    *p5 = 10;
    // p 接收 p5 释放的堆内存
    int * p = p5.release();
    cout << *p << endl; // 10
    //判断 p5 是否为空指针
    if (p5) {
        cout << "p5 is not nullptr" << endl;
    }
    else {
        cout << "p5 is nullptr" << endl; // null 
    }

    std::unique_ptr<int> p6;
    //p6 获取 p 的所有权
    p6.reset(p);
    cout << *p6 << endl; // 10
    return 0;
}
  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值