五家渠市网站建设_网站建设公司_Django_seo优化
2026/1/17 8:37:20 网站建设 项目流程

一、使用

1. 抛异常的安全性

int calc(int a, int b) { if (b == 0) { throw("by 0"); } else { return a / b; } } void func() { int* p1 = new int[10]; calc(1, 0); }
  • 在这个函数中,calc抛出异常,int数组就不会被析构,造成内存泄露。

  • 除非在每一个new后面都跟着一个delete[] p1;

2. RAII 和智能指针设计思路

  • 利用对象生命周期管理资源的获取和释放,将资源托管给类的析构函数。

  • 并且重载了*->等运算符进行调用,使其表现得像指针。

int calc(int a, int b) { if (b == 0) { throw("by 0"); } else { return a / b; } } void func() { SmartPtr<int> p1 = new int[10]; calc(1, 0); }
  • 异常时,智能指针对象一定会析构,就能带着其管理的int*资源一起析构。

3. C++ 标准库的智能指针

  • 拷贝问题:由于智能指针是仿指针,因此拷贝也需要仿照指针进行浅拷贝,但这会带来析构两次的问题。

  1. auto_ptr: C++98 的智能指针,极其不推荐使用,因为它会让被拷贝的源指针悬空。
    https://media/image1.png

  2. unique_ptr: 不能拷贝,除非被move

  3. shared_ptr: 用引用计数实现拷贝。

    shared_ptr<int> p1(new int[10]); shared_ptr<int> p2 = p1; cout << p1.use_count();
    • 还可以通过use_count()查看引用计数。

二、shared_ptr 实现

1. 成员变量

T* _ptr; int* _count;
  • 由于静态变量会统计所有智能指针的个数,而智能指针可能会指向不同的资源,因此不能用静态变量来计数。

  • 需要使用动态开辟的指针,当构造时就开辟新的空间来存储计数。

2. 构造函数

shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) ,_count(sp._count) { (*_count)++; } shared_ptr(T* ptr) :_ptr(ptr) { _count = new int(1); }
  • 拷贝构造时,引用计数加一。

  • 用原始指针构造时,新开一块空间对_count进行初始化计数。

3. 析构函数

~shared_ptr() { if (--(*_count) == 0) { delete _ptr; delete _count; } }
  • 只有引用计数到达 0 时才释放管理的资源。

4. 解引用操作

T& operator*() { return *_ptr; } T* operator->() { return _ptr; }

5. 获取引用计数

int use_count() { return *_count; }

6. 赋值运算符重载(重点)

  • 需要先处理当前智能指针管理的资源(如果引用计数到 0 则析构)。

  • 然后接收新的资源,并对新的引用计数加一。

shared_ptr<T>& operator=(const shared_ptr<T>& sp) { if (sp._ptr != _ptr) { if (--(*_count) == 0) { delete _ptr; delete _count; } _count = sp._count; _ptr = sp._ptr; ++(*_count); } return *this; }

三、定制删除器

  • 由于智能指针指向的资源不一定是new出来的单个T类型对象,还可能是T数组,甚至是打开的文件句柄。

  • 直接用deletedelete[]释放可能会出错。

  1. 模板特化

    • 对于数组,可以使用shared_ptr<Date[]>,库会进行模板特化,调用delete[]

      shared_ptr<Date[]> d1(new Date[10]);
  2. 仿函数

    • 对于文件等资源,delete无法释放。

      shared_ptr<FILE> p3(fopen("源.cpp", "r")); // 错误,会造成内存泄露
    • 需要定义删除器。

      struct del { void operator()(FILE* fp) const { if (fp) { fclose(fp); std::cout << "文件已关闭" << std::endl; } } }; shared_ptr<FILE> p3(fopen("源.cpp", "r"), del());
  3. unique_ptr的删除器

    • unique_ptr的删除器类型需要在模板参数中指定,而不是在构造函数中传入。

      unique_ptr<FILE, del> p4(fopen("源.cpp", "r"));
    • 也可以使用lambda表达式,但需要用decltype推导其类型。

      auto fclosefunc = [](FILE* ptr) { fclose(ptr); }; unique_ptr<FILE, decltype(fclosefunc)> p5(fopen("源.cpp", "r"), fclosefunc);
  4. 模拟实现定制删除器

    • 由于需要在构造函数中接收仿函数,可以用function包装器来存储。

      function<void(T*)> _del = [](T* t) { delete t; };
    • 提供两个构造函数重载,区分是否传入自定义删除器。

      shared_ptr(T* ptr) :_ptr(ptr) { _count = new int(1); } template <class D> shared_ptr(T* ptr, D del) : _ptr(ptr) , _del(del) { _count = new int(1); }
    • 析构时调用包装器存储的函数。

      ~shared_ptr() { if (--(*_count) == 0) { _del(_ptr); delete _count; } }
  5. make_shared

    • 由于shared_ptr需要为对象和控制块(包含引用计数等)分别开辟空间,可能会产生内存碎片。

    • make_shared可以将这两个部分一次性连续开辟,减少碎片。

      cpp

      shared_ptr<Date> p6 = make_shared<Date>();

四、循环引用

  • 考虑在双向链表中使用shared_ptr

    struct ListNode { int _data; ListNode* _next; ListNode* _prev; ~ListNode() { cout << "~ListNode()" << endl; } }; shared_ptr<bit::ListNode> p1(new bit::ListNode); shared_ptr<bit::ListNode> p2(new bit::ListNode); p1->_next = p2; // 错误,类型不匹配
  • 在链表的节点里,如果要用智能指针管理,由于_next为普通指针,p2为智能指针,因此无法直接赋值。

  • 需要将节点内的指针也改为智能指针类型。

    struct ListNode { int _data; std::shared_ptr<ListNode> _next; std::shared_ptr<ListNode> _prev; ~ListNode() { cout << "~ListNode()" << endl; } };
  • 但是,这样做会导致内存泄露!

  • 原因分析:

    • 第一个节点的资源由p1p2_prev指向,引用计数为 2。

    • 第二个节点的资源由p2p1_next指向,引用计数也为 2。

    • 函数结束时,p1p2先析构,两个节点的引用计数都变为 1。

    • 接着,第一个节点要析构,需要等待第二个节点的_prev析构。

    • 第二个节点要析构,需要等待第一个节点的_next析构。

    • 这就陷入了循环等待,导致两个节点都无法被释放。

  • 解决方案:weak_ptr

    • 将节点内的指针改为weak_ptr

      struct ListNode { int _data; std::weak_ptr<ListNode> _next; std::weak_ptr<ListNode> _prev; ~ListNode() { cout << "~ListNode()" << endl; } };
    • weak_ptr指向资源但不会增加引用计数,从而打破了循环引用,资源可以正常析构。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询