C++ 编程规范-智能指针

本文详细介绍了C++11中的智能指针std::shared_ptr,包括其基本用法、引用计数原理、make_shared的高效性和使用场景,并通过实例展示了如何避免内存泄漏,提高代码安全性和效率。强调了std::shared_ptr在管理动态分配对象时的优势以及避免错误使用的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在传统 C++ 里我们只好使用 new 和 delete 去 『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。 这些智能指针包括 std::shared_ptr/std::unique_ptr/std::weak_ptr,使用它们需要包含头文件 。

shared_ptr

std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数, 并通过use_count()来查看一个对象的引用计数。例如:

1. 一个简单的例子

auto pointer = std::make_shared<int>(10);
auto pointer2 = pointer; // 引用计数+1
auto pointer3 = pointer; // 引用计数+1
// std::shared_ptr<int> p(pointer);  // 当然也可以这样托管,引用计数+1
int *p = pointer.get();  // 这样不会增加引用计数
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;   // 3
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3

pointer2.reset();
std::cout << "reset pointer2:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;   // 2
std::cout << "pointer2.use_count() = "
          << pointer2.use_count() << std::endl;           // pointer2 已 reset; 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2

pointer3.reset();
std::cout << "reset pointer3:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;   // 1
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
std::cout << "pointer3.use_count() = "
          << pointer3.use_count() << std::endl;           // pointer3 已 reset; 0

在这里插入图片描述

2. 一个详细的例子

只要将 new 运算符返回的指针 p 交给一个 shared_ptr 对象“托管”,就不必担心在哪里写delete p语句——实际上根本不需要编写这条语句,托管 p 的 shared_ptr 对象在消亡时会自动执行delete p。而且,该 shared_ptr 对象能像指针 p —样使用,即假设托管 p 的 shared_ptr 对象叫作 ptr,那么 *ptr 就是 p 指向的对象。

通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:

shared_ptr<T> ptr(new T);  // T 可以是 int、char、类等各种类型

此后,ptr 就可以像 T* 类型的指针一样使用,即 *ptr 就是用 new 动态分配的那个对象。
多个 shared_ptr 对象可以共同托管一个指针 p,当所有曾经托管 p 的 shared_ptr 对象都解除了对其的托管时,就会执行delete p。

例如:

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
    int i;
    A(int n):i(n) { };
    ~A() { cout << i << " " << "destructed" << endl; }
};
int main()
{
    shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
    
    // 其实上边这步可以拆成两步,意思是一样的,同样当sp1消亡的时候,会自动析构tmp指针。
    A* tmp = new A(2);
    shared_ptr<A> sp1(tmp);
    // 但是不能同时两个共同托管一个普通指针,这种情况,两个智能指针互不知道,消亡的时候会两次析构。如下所示:
    shared_ptr<A> sp11(tmp);

    shared_ptr<A> sp2(sp1);       //A(2)同时交由sp2托管
    shared_ptr<A> sp3;
    sp3 = sp2;   //A(2)同时交由sp3托管
    cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
    A * p = sp3.get();      // get返回托管的指针,p 指向 A(2)
    cout << p->i << endl;  //输出 2
    sp1.reset(new A(3));    // reset导致托管新的指针, 此时sp1托管A(3)
    sp2.reset(new A(4));    // sp2托管A(4)
    cout << sp1->i << endl; //输出 3
    sp3.reset(new A(5));    // sp3托管A(5),A(2)无人托管,被delete
    cout << "end" << endl;
    return 0;
}

输出结果:

2,2,2
2
3
2 destructed  // sp3.reset(new A(5))的时候,A(2)对象被delete.
end
5 destructed  // 程序执行完毕,剩余对象被delete.
4 destructed
3 destructed

注意:只有指向动态分配的对象的指针才能交给 shared_ptr 对象托管。将指向普通局部变量、全局变量的指针交给 shared_ptr 托管,编译时不会有问题,但程序运行时会出错,因为不能析构一个并没有指向动态分配的内存空间的指针

注意,不能用下面的方式使得两个 shared_ptr 对象托管同一个指针:

A* p = new A(10);
shared_ptr <A> sp1(p), sp2(p);

sp1 和 sp2 并不会共享同一个对 p 的托管计数,而是各自将对 p 的托管计数都记为 1(sp2 无法知道 p 已经被 sp1 托管过)。这样,当 sp1 消亡时要析构 p,sp2 消亡时要再次析构 p,这会导致程序崩溃。

3. make_shared的理解

https://blog.csdn.net/weixin_39956356/article/details/110650024

当然,使用智能指针托管的时候还可以使用make_shared来快速生成智能指针。通常make_shared()函数比直接创建shared_ptr对象要高效,且只分配一次内存。

  • 方式一,共享指针既管理对象本身,又管理包含引用计数和其他内务数据的小对象。make_shared可以分配单个内存块来容纳这两个对象;从指向已经分配的对象的指针构造共享指针将需要分配第二个内存块来存储引用计数。
  • 方式二,就不要使用了,要动用两次动态内存分配
  • 除了这种效率,使用make_shared意味着您根本不需要处理new和原始指针,从而提供了更好的异常安全性-在分配对象之后但在将其分配给智能指针之前,不可能引发异常。
std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

```cpp
// 方式一:使用make_shared
auto sp = boost::make_shared<std::string>("make_shared123");  
// 方式二:
shared_ptr<std::string> sp(new std::string("make_shared123"));

auto sp1 = new std::string("xxxx");
std::cout << "use_count: " << sp.use_count() << std::endl;
sp.reset(sp1);
std::cout << "use_count: " << sp.use_count() << std::endl;
std::cout << "*sp: " << *sp << std::endl;

输出:

use_count: 1
use_count: 1  // sp托管的指针被几个智能指针托管
*sp: xxxx

拿自定义对象来演示快速创建shared_ptr对象

class Algo {
public:
    Algo(int i): id(i) {}
private:
	int id;
};

std::shared_ptr<Algo> algo_ptr = std::make_shared<Algo>(1);

4. 更多待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值