当前位置: 首页 > article >正文

大话C++:第28篇 详解独占智能指针

1 智能指针概述

C++中的智能指针是一种用于管理原生指针(raw pointer)生命周期的对象。智能指针的引入主要是为了帮助开发者避免原生指针在使用过程中可能产生的一些问题,如内存泄漏、野指针(dangling pointer)等。C++11及之后的版本提供了多种智能指针类型,包括:

  • std::unique_ptr:这是一种独占所有权的智能指针,它负责对象的生命周期。当std::unique_ptr被销毁时,它所指向的对象也会被自动销毁。这种智能指针保证同一时间只能有一个std::unique_ptr指向同一个对象。它不能被复制到其他std::unique_ptr,但可以通过std::move来转移所有权。

  • std::shared_ptr:这是一种共享所有权的智能指针。多个std::shared_ptr可以指向同一个对象,并且它们会共同管理该对象的生命周期。当最后一个指向对象的std::shared_ptr被销毁时,对象才会被销毁。这种智能指针使用引用计数来实现共享所有权。

  • std::weak_ptr:这是std::shared_ptr的一个辅助工具,用于解决std::shared_ptr可能产生的循环引用问题。std::weak_ptr可以从一个std::shared_ptr或另一个std::weak_ptr对象构造,但它的构造或析构都不会影响引用计数。只有当std::weak_ptr升级为std::shared_ptr时,引用计数才会增加。

使用智能指针时,应尽量避免使用原生指针,因为智能指针能自动管理内存,从而减少出错的可能性。当需要将智能指针传递给不支持智能指针的API时,可以使用智能指针的.get()成员函数获取原生指针,但这种情况下需要格外小心,以避免内存泄漏和其他问题。

2 std::unique_ptr

std::unique_ptr 是 C++11 引入的一种智能指针类型,用于管理动态分配的内存。它表示“独占”所有权的概念,即 std::unique_ptr 所指向的对象在同一时间只能由一个 std::unique_ptr 拥有。当 std::unique_ptr 被销毁时(例如超出作用域或被显式删除),它所指向的对象也会被自动销毁。

std::unique_ptr 的主要操作和成员函数:

操作/成员函数描述
构造函数
unique_ptr()默认构造函数,创建一个空的 unique_ptr,不拥有任何对象。
unique_ptr(pointer p) noexcept接收一个原生指针 p,并接管其所有权。
unique_ptr(nullptr_t) noexcept接收 nullptr,创建一个空的 unique_ptr
unique_ptr(unique_ptr&& u) noexcept移动构造函数,从另一个 unique_ptr u 接管对象的所有权。
析构函数
~unique_ptr()析构函数,销毁 unique_ptr 所拥有的对象,并释放内存。
赋值操作
unique_ptr& operator=(pointer p) noexcept重置 unique_ptr 以拥有原生指针 p 指向的对象。
unique_ptr& operator=(nullptr_t) noexcept重置 unique_ptr 为空,释放当前拥有的对象。
unique_ptr& operator=(unique_ptr&& u) noexcept移动赋值,从另一个 unique_ptr u 接管对象的所有权。
重置操作
void reset(pointer p = pointer()) noexcept重置 unique_ptr 以拥有原生指针 p 指向的对象,或如果 pnullptr,则释放当前对象。
观察操作
pointer get() const noexcept返回 unique_ptr 所拥有的对象的原生指针。
operator bool() const noexcept隐式转换为 bool,检查 unique_ptr 是否拥有一个对象(非空)。
reference operator*() const解引用 unique_ptr 所拥有的对象。
pointer operator->() const noexcept返回指向 unique_ptr 所拥有的对象的原生指针。
释放操作
pointer release() noexcept释放 unique_ptr 对对象的所有权,并返回原生指针。此后,unique_ptr 不再拥有该对象。

注意pointerreferencenullptr_t 是类型别名,分别代表 T*T&std::nullptr_t,其中 Tunique_ptr 所管理的对象类型。

综合案例,代码示例如下:

#include <iostream>
#include <memory>
#include <string>


class Data
{
public:
    // 构造函数
    Data(const std::string& value) 
        : value(value)
        , addr(new std::string(value))
    {
        std::cout << "调用Data构造函数" << std::endl;
    }

    // 拷贝构造函数
    Data(const Data& other)
    {
        value = other.value;
        // 注意,深拷贝
        addr = new std::string(*other.addr);
    }

    // 赋值运算符
    Data& operator=(const Data& other)
    {
        if (this != &other)
        {
            value = other.value;
            // 注意,深拷贝
            addr = new std::string(*other.addr);
        }

        return *this;
    }

    // 析构函数
    ~Data()
    {
        std::cout << "调用析构函数" << std::endl;
        std::cout << value << std::endl;

        // 注意,如果类里采用了原生指针的话,new的对象,需要在析构函数里手动释放
        if (addr != nullptr)
        {
            delete addr;
            addr = nullptr;
        }
    }

    std::string GetValue() const 
    {
        return value;
    }
    
    void SetValue(const std::string& value)
    {
        this->value = value;
    }

    void Show() const 
    {
        std::cout << "value=" << value << std::endl;
    }


private:
    std::string value;
    std::string *addr;
};


int main()
{
    // 创建unique_ptr对象
    // 1.new创建对象
    std::unique_ptr<Data> uptr1(new Data("new obj1"));
    // 2.std::make_unique,推荐方式
    std::unique_ptr<Data> uptr2 = std::make_unique<Data>("make_unique obj2");

    // unique_ptr对象赋值给另外一个对象呢?
    // 典型禁用了unique_ptr的赋值运算符operator=
    //  errror:use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=
    // (const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Data; _Dp = std::default_delete<Data>]’
    // std::unique_ptr<Data> uptr4;
    // uptr4 = uptr2;

    // 指针形式,访问对象的成员函数
    uptr1->Show();
    uptr2->Show();

    // 解引用unique_ptr
    // 相当于对象自身
    // 例如,Data obj;
    // obj.GetValue()
    std::cout << "uptr1对象的内容:" << (*uptr1).GetValue() << std::endl;

    // 获取原生指针
    Data *rawPtr = uptr1.get();
    std::cout << "原生指针指向对象的内容:" << rawPtr->GetValue() << std::endl;

    // 对象控制权的转移
    // 利用移动构造函数和移动语义std::move
    std::unique_ptr<Data> uptr3 = std::move(uptr2);
    uptr3->Show();

    // 对象释放
    // 1.reset释放原有对象,要么指向nullptr,要么重新指向新的对象
    uptr3.reset(new Data("reset obj3"));
    uptr3->Show();

    // 2.release()释放原有对象
    uptr3.release();
    if (uptr3 != nullptr)
    {
        std::cout << "uptr3对象没有被释放了" << std::endl;
    }
    else 
    {
        std::cout << "uptr3对象已经被释放了" << std::endl;
    }
    

    return 0;
}


http://www.kler.cn/a/401199.html

相关文章:

  • 【Spring】循环引用 解决流程,只用一二级缓存?
  • Android集成FCM(Firebace Cloud Messaging )
  • mysql 的乐观锁和 mvcc 是一回事吗
  • Debezium-MySqlConnectorTask
  • 【AI+教育】一些记录@2024.11.11
  • 【C/C++】Lambda 用法
  • Vue3中使用:deep修改element-plus的样式无效怎么办?
  • 【算法】P5018 对称二叉树
  • 基于YOLOv8深度学习的智慧课堂教师上课行为检测系统研究与实现(PyQt5界面+数据集+训练代码)
  • gvim添加至右键、永久修改配置、放大缩小快捷键、ctrl + c ctrl +v 直接复制粘贴、右键和还原以前版本(V)冲突
  • 《原子与分子物理学报》
  • 玩转view和text组件与相关动画的使用
  • 如何在 Ubuntu 上设置 JAVA_HOME 环境变量 ?
  • 青蛙跳台阶
  • Python酷库之旅-第三方库Pandas(229)
  • MySQL数据库学习(持续更新ing)
  • Qt MDI与Splash介绍
  • 使用pandoc将latex转换成word(带参考文献)
  • uni-app获取安全区域
  • 基于centos7.9搭建tmall商城
  • GRU(门控循环单元)详解
  • 图片画廊4 -- 使用Owl Carousel进行优化
  • 探索Python PDF处理的奥秘:pdfrw库揭秘
  • 设计模式之组合模式(营销差异化人群发券,决策树引擎搭建场景)
  • Excel——宏教程(2)
  • 基于Matlab的变压器仿真模型的建模方法(2):单相双绕组变压器的状态方程和仿真模型(附源代码)