C++智能指针的使用

智能指针是C++中的资源管理工具,如unique_ptr、shared_ptr和weak_ptr,用于自动释放对象,防止内存泄漏。unique_ptr独占资源,不允许拷贝,而shared_ptr允许多个指针共享资源,通过引用计数管理。weak_ptr作为shared_ptr的辅助,不增加引用计数,防止循环引用导致的内存泄漏问题。

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

1、定义

智能指针(RAII-Resource Acquisition Is Initialization),资源获取即初始化机制对普通指针的一种封装,这样使得智能指针的行为动作像一个指针。本质上却是一个对象,这样可以方便管理一个对象的生命周期。

在c++中智能指针一共定义了四种:unique_ptr、shared_ptr、auto_ptr和weak_ptr。其中,auto_ptr在C++11已被摒弃,在C++17中已经移除不可用。

2、原始指针的问题

当指针未删除,或者删除的情况未考虑清除,容易造成悬挂指针(danging pointer)和野指针(wild  pointer)的现象

注:悬挂指针(野指针):指针指向非法的内存地址,原因一般有

a、使用未初始化的指针

b、指针所指的对象已消亡

c、指针释放后未被置空

因此,使用普通指针我们经常会遇到各种问题,需要手动初始化和释放,智能指针可以有效避免这些问题。

3、unique_ptr

初始化:有四种构造方式,unique_ptr和shared_ptr两者的构造函数声明均为explicit,即不允许隐式类型转换。unique_ptr不允许拷贝构造

①unique_ptr<T> up;up.reset(new T());

②unique_ptr<T> up(new T());

③unique_ptr<T> up1 = std::move(up);//移动构造

④auto up = make_unique<int>(10);

struct Task {
    int mId;
    Task(int id) :mId(id)
    {
        std::cout << "Task::constructor" << std::endl;
    }
    ~Task()
    {
        std::cout << "Task::Destructor" << std::endl;
    }
};

int main()
{
    unique_ptr<Task> taskPtr(new Task(23));
    return 0;
}

输出结果:

Task::constructor
Task::Destructor

不管函数正常退出还是异常退出,也始终会调用taskPtr的析构函数。因此,原始指针将始终被删除并防止内存泄漏。

特性:unique_ptr独享所有权

unique_ptr始终是关联原始指针的唯一所有者,无法复制unique_ptr对象,只能移动。每个unique_ptr都是原始指针的唯一拥有者,因此在其析构函数中直接删除相关联的指针。我们无法复制unique_ptr对象,但可以转移他们。

int main()
{
    unique_ptr<Task> taskPtr(new Task(23));

    unique_ptr<Task> taskPtr4 = std::move(taskPtr);

    if (taskPtr == nullptr)
    {
        std::cout << "data convert success" << std::endl;
    }

    return 1;
}

输出结果:

Task::constructor
data convert success
Task::Destructor

尽管重新定义了一个新变量taskPtr4,但是只有一次构造和析构。

独有操作:

up.release(); up放弃对指针的控制权,返回裸指针,并将up置空

up.reset(); 释放up指向的对象

up.reset(q):其中q为裸指针,令up指向q所指对象

4、shared_ptr

初始化:

①shared_ptr<T> sp;sp.reset(new T());

②shared_ptr<T> sp(new T());

③shared_ptr<T> sp1 = sp; //拷贝构造

④auto sp = make_shared<int>(10);

独有操作:

①shared_ptr<T> p(q);//拷贝构造

②p = q;//赋值

③p.unique();若p.use_count()为1,返回true,否则返回false。

④p.use_count()//返回强引用计数

    shared_ptr<Task> sharePtr(new Task(25));

    std::cout << sharePtr.use_count() << endl;

    shared_ptr<Task> sharePtr4 = sharePtr;

    std::cout << sharePtr.use_count() << endl;

返回结果:

1
2

5、weak_ptr

weak_ptr虽然是智能指针,但实际上作为shared_ptr的辅助指针使用。weak_ptr不会影响shared_ptr对象的引用计数。

成员函数:

  • reset()使weak_ptr对象变为空指针,类似默认构造函数。(与shared_ptr,unique_ptr的reset不同,weak_ptr的reset不能带参数)。
  • swap(std::weak_ptr<T> &p)交换两个weak_ptr对象的内部指针。
  • use_count()查看关联shared_ptr的引用计数。
  • expired()判断weak_ptr是否过期。如果weak_ptr为空,或者关联的shared_ptr为空,返回true。
  • lock()如果weak_ptr对象已过期,返回一个空shared_ptr对象;否则,返回一个weak_ptr内部指针相关联的shared_ptr对象(因此,shared_ptr对象的引用计数+1)。

使用shared_ptr在循环引用时会出现内存泄漏的情况,由于循环引用,use_count不会在代码块结束时降为0,shared_ptr指向的内存不会被释放。

class A;
class B;

class A
{
public:
    std::shared_ptr<B> b_;
public:
    A() {
        std::cout << "construct A" << std::endl;
    }
    ~A() {
        std::cout << "destroy A" << std::endl;
    }
};

class B
{
public:
    std::shared_ptr<A> a_;
public:
    B() {
        std::cout << "construct B" << std::endl;
    }
    ~B() {
        std::cout << "destroy B" << std::endl;
    }
};

结果可以看到析构没有被调用

construct A
construct B
A use_count is 1
B use_count is 1
A use_count is 2
B use_count is 2

使用weak_ptr声明对象成员变量可以避免这种问题

construct A
construct B
A use_count is 1
B use_count is 1
A use_count is 1
B use_count is 1
destroy B
destroy A

参考:

一篇文章说清楚shared_ptr,unique_ptr的区别和关系_燕山暮雪的博客-CSDN博客_unique_ptr

C++ 智能指针 unique_ptr 详解与示例_hhy980205的博客-CSDN博客_c++ unique_ptr

C++学习 三、weak_ptr使用_RuiH.AI的博客-CSDN博客_c++ weak_ptr

C++智能指针weak_ptr详解_物随心转的博客-CSDN博客_std::weak_ptr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值