C++的智能指针unique_ptr、shared_ptr和weak_ptr

2023-11-08

C++的智能指针是一种特殊的指针类型,它能够自动管理内存资源,避免常见的内存泄漏和多次释放等问题。C++11引入了三种主要的智能指针:unique_ptr、shared_ptr和weak_ptr。

目录

①unique_ptr

②shared_ptr

③weak_ptr


①unique_ptr

在C++中,unique_ptr是一个智能指针(smart pointer)类模板,用于管理动态分配的内存资源,它提供了自动释放内存的功能。与原始指针相比,unique_ptr有更高的安全性和易用性。

unique_ptr具有以下特点:

独占所有权:每个unique_ptr实例拥有对其所指向对象的唯一所有权。这意味着在任何时候只有一个unique_ptr可以指向一个特定的对象。

自动释放内存:当unique_ptr超出作用域或被重新赋值时,它所管理的内存会自动释放。这样就避免了内存泄漏的问题。

指针语义:unique_ptr的使用方式与原始指针相似,可以通过指针操作符(->)和解引用操作符(*)来访问所指向对象的成员。

不可拷贝:unique_ptr是不可拷贝的,即不能进行复制构造和赋值操作。这是为了确保独占所有权的特性,防止多个指针同时管理同一个对象的内存。

支持移动语义:unique_ptr支持移动构造和移动赋值操作,可以将所有权转移给新的unique_ptr,而无需进行内存拷贝。

可自定义删除器:unique_ptr可以通过模板参数来指定一个删除器(deleter)函数对象,用于在释放内存时执行额外的清理操作。

示例代码:

#include <memory>

int main() {
    // 创建一个unique_ptr,指向一个动态分配的int对象
    std::unique_ptr<int> ptr(new int(42));

    // 使用指针操作符和解引用操作符访问所指向对象的值
    std::cout << *ptr << std::endl;  // 输出: 42

    // 通过移动构造函数将所有权转移给另一个unique_ptr
    std::unique_ptr<int> ptr2 = std::move(ptr);

    // 注意,此时ptr已经为空指针,不再拥有对象的所有权
    std::cout << *ptr2 << std::endl;  // 输出: 42

    // 使用自定义删除器
    struct Deleter {
        void operator()(int* p) {
            std::cout << "Custom deleter called" << std::endl;
            delete p;
        }
    };

    std::unique_ptr<int, Deleter> ptr3(new int(100), Deleter());

    // unique_ptr超出作用域时会自动释放内存,同时调用自定义删除器
    return 0;
}

常见成员函数

operator*:解引用操作符,用于获取 unique_ptr 所指向对象的引用。

operator->:箭头操作符,用于通过 unique_ptr 访问对象的成员函数或成员变量。

get:返回指向所管理对象的裸指针。

reset:重置 unique_ptr,释放当前所管理的对象并接管新的对象。

release:释放对所管理对象的控制权,并返回该指针的裸指针。

swap:交换两个 unique_ptr 的内容。

②shared_ptr

在C++中,shared_ptr是一个智能指针(smart pointer)类模板,用于管理动态分配的内存资源。与unique_ptr相比,shared_ptr可以实现多个指针共享同一块内存,并且提供了自动释放内存的功能。

shared_ptr具有以下特点:

共享所有权:多个shared_ptr实例可以同时指向同一个对象,它们共享对所指向对象的所有权。只有当所有shared_ptr都超出作用域或被重新赋值时,才会释放所管理的内存。

自动释放内存:当最后一个指向对象的shared_ptr超出作用域或被重新赋值时,它会自动释放所管理的内存。这种机制称为引用计数(reference counting),通过计数器来追踪当前有多少个shared_ptr指向同一块内存。

指针语义:shared_ptr的使用方式与原始指针相似,可以通过指针操作符(->)和解引用操作符(*)来访问所指向对象的成员。

可拷贝:shared_ptr是可拷贝的,即可以进行复制构造和赋值操作。每次拷贝会增加引用计数。当引用计数变为0时,表示没有任何shared_ptr指向该内存,会释放内存。

循环引用问题:如果存在循环引用(两个或多个对象相互持有shared_ptr),会导致内存泄漏。为了解决这个问题,可以使用弱引用指针weak_ptr。

示例代码:

#include <memory>

int main() {
    // 创建一个shared_ptr,指向一个动态分配的int对象
    std::shared_ptr<int> ptr1(new int(42));

    // 使用指针操作符和解引用操作符访问所指向对象的值
    std::cout << *ptr1 << std::endl;  // 输出: 42

    // 复制构造函数,共享同一块内存
    std::shared_ptr<int> ptr2 = ptr1;

    // 增加引用计数
    std::cout << ptr1.use_count() << std::endl;  // 输出: 2

    // 通过弱引用指针weak_ptr解决循环引用问题
    std::weak_ptr<int> weakPtr = ptr1;

    // 使用lock()函数获取一个shared_ptr
    std::shared_ptr<int> ptr3 = weakPtr.lock();
    if (ptr3 != nullptr) {
        // 成功获取shared_ptr
    }

    // 减少引用计数
    ptr1.reset();

    return 0;
}

常见成员函数

operator*:解引用操作符,用于获取 shared_ptr 所指向对象的引用。

operator->:箭头操作符,用于通过 shared_ptr 访问对象的成员函数或成员变量。

get:返回指向所管理对象的裸指针。

reset:重置 shared_ptr,释放当前所管理的对象并接管新的对象。

release:释放对所管理对象的控制权,并返回该指针的裸指针。

swap:交换两个 shared_ptr 的内容。

use_count:返回当前被所有 shared_ptr 指向的对象的引用计数。

③weak_ptr

在 C++ 中,weak_ptr 是一种智能指针(smart pointer),用于解决循环引用问题。它是由 shared_ptr 派生而来,但不会增加引用计数,只是对所指向对象进行观察,并不拥有对象的所有权。

循环引用问题

循环引用问题指的是在使用shared_ptr管理对象时,存在两个或多个对象相互持有shared_ptr,形成一个循环引用的情况。这种情况下,每个对象的引用计数都不会变为0,导致内存泄漏。

具体来说,当两个对象相互持有shared_ptr时,它们的引用计数始终大于0,因此它们所指向的内存块永远不会被释放。即使程序使用结束,这部分内存也无法回收,造成了内存泄漏的问题。

循环引用问题的实际场景可能是两个对象之间存在双向关联,比如A对象持有shared_ptr指向B对象,而B对象也持有shared_ptr指向A对象。当这两个对象的生命周期延长,超过了程序实际需要它们的时间时,就会造成循环引用和内存泄露。

为了解决循环引用问题,C++中引入了弱引用指针weak_ptr。弱引用指针和shared_ptr不同,它不会增加引用计数,只是对所指向对象进行观察,并不拥有对象的所有权。通过弱引用指针,我们可以在需要时使用lock()函数获取一个有效的shared_ptr来操作对象,一旦对象的引用计数变为0,弱引用指针将自动失效。

使用弱引用指针可以破坏循环引用,让所有的shared_ptr都能够正常析构并释放所管理的内存,避免了潜在的内存泄漏风险。

weak_ptr 具有以下特点和用法

弱引用:因为 weak_ptr 不会增加引用计数,所以当所有 shared_ptr 都释放后,weak_ptr 将自动失效。它允许你观察一个对象,但不影响其生命周期。

通过 shared_ptr 创建:通常,我们使用 shared_ptr 来初始化 weak_ptr。这样可以确保 weak_ptr 观察的对象仍然存在。

使用 lock() 获取 shared_ptr:要操作 weak_ptr 所观察的对象,可以使用 lock() 函数获取一个有效的 shared_ptr。如果原始的 shared_ptr 已经被释放,lock() 返回一个空的 shared_ptr。

判断是否有效:可以使用 expired() 函数来检查 weak_ptr 是否已经失效,即所观察的 shared_ptr 是否已经被释放。

解决循环引用问题:由于 weak_ptr 不增加引用计数,可以用于解决两个或多个对象之间的循环引用问题,避免内存泄漏。

示例代码:

#include <memory>
#include <iostream>

class B; // 前置声明

class A {
public:
    std::shared_ptr<B> bPtr;

    ~A() { std::cout << "A destroyed" << std::endl; }
};

class B {
public:
    std::weak_ptr<A> aWeakPtr;

    ~B() { std::cout << "B destroyed" << std::endl; }
};

int main() {
    std::shared_ptr<A> aPtr = std::make_shared<A>();
    std::shared_ptr<B> bPtr = std::make_shared<B>();

    aPtr->bPtr = bPtr;
    bPtr->aWeakPtr = aPtr;

    std::cout << "Use count of aPtr: " << aPtr.use_count() << std::endl;
    std::cout << "Use count of bPtr: " << bPtr.use_count() << std::endl;

    // 使用 lock() 获取有效的 shared_ptr
    std::shared_ptr<A> lockedAPtr = bPtr->aWeakPtr.lock();
    if (lockedAPtr) {
        // 使用 lockedAPtr 操作所观察的对象
        std::cout << "A exists" << std::endl;
    } else {
        std::cout << "A does not exist" << std::endl;
    }

    return 0;
}

常见成员函数

expired:检查 weak_ptr 所观察的 shared_ptr 是否已经失效。

lock:获取一个有效的 shared_ptr,用于操作所观察的对象。如果原始的 shared_ptr 已经被释放,返回一个空的 shared_ptr。

use_count:返回当前被所有 shared_ptr 指向的对象的引用计数。

reset:重置 weak_ptr。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++的智能指针unique_ptr、shared_ptr和weak_ptr 的相关文章

随机推荐

  • 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。

    定义栈的数据结构 请在该类型中实现一个能够得到校的最小元素的min函数 在该栈中 调用pop push 及min的时间复杂度都是0 1 param
  • pyqt5 QGraphicsView内缩放显示的图像

    from PyQt5 QtWidgets import QApplication QGraphicsView QGraphicsScene QGraphicsItem QGraphicsPixmapItem from PyQt5 QtCor
  • log4j-slf4j-impl cannot be present with log4j-to-slf4j 之类的问题,解决maven依赖冲突

    如题所示 本文主要用户解决maven的依赖冲突 并提供通用的解决方案 先放错误 SLF4J Class path contains multiple SLF4J bindings SLF4J Found binding in jar fil
  • 【Android】Room新手快速入门

    Room是什么 Room是Google推出的一款android平台上的ORM数据库框架 它类似于GreenDao 但比GreenDao更加简洁高效 是官方推荐使用的数据库框架 引入Gradle依赖 Room api androidx roo
  • 私有仓库修改docker.json报错

    我试了目前网上所有方法都不行自己摸索了很久 提供一种解决方案 找到你的阿里云镜像加速器 重新把配置阿里云镜像的那几行linux代码 再执行一次 然后紧接着马上去 etc docker daemon json加你的私有仓库http配置 再重启
  • NUC980开源项目31-can总线调试

    上面是我的微信和QQ群 欢迎新朋友的加入 以CAN0为例 在内核中配置 文件系统支持 重新烧录 启动 正常挂载 CAN终端测试 查看CAN接口 root myir ls sys class net can0 eth0 lo usb0 wla
  • docker部署fisco bcos区块链浏览器

    首先你要搭建一个myql数据库 理论上mariadb也支持 并创建一个数据库 再建个账号给区块链浏览器使用 如 CREATE DATABASE fisco CHARACTER SET utf8mb4 COLLATE utf8mb4 bin
  • [Excel VBA]如何自动关闭MsgBox?

    本文译至 http itpro nikkeibp co jp atcl column 15 090100207 090700150 VBA的MsgBox函数 直到用户点击按钮前都会继续显示 经过指定时间后自动关闭的MsgBox 可以使用Wi
  • 中高级程序员需求技能

    初级 中级 1 团队精神和协作能力 把它作为基本素质 并不是不重要 恰恰相反 这是程序员应该具备的最基本的 也是最重要的安身立命之本 把高水平程序员说成独行侠的都是在呓语 任何个人的力量都是有限的 即便如linus这样的天才 也需要通过组成
  • Unity发送Post请求

    using System Collections using System Text using UnityEngine using UnityEngine Networking public class PostMsg MonoBehav
  • CSDN竞赛第34期题解

    CSDN竞赛第34期题解 1 题目名称 最优利润值 你在读的经营课程上 老师布置了一道作业 在一家公司的日常运营中 是会对一些商品的价格走势根据一些经验和数据 进行预估 并据此进行决策 例如 假设某商品每天的价格都有可能变动 我们要做的就是
  • 从URL取值传给后端

    从URL传值给后端 http 127 0 0 1 8080 blog content html id 8 点击浏览文章详情 跳转至详情页面 从 url 中拿出文章 id 传给后端 首先拿到url 然后判断是否有值 从问号后面取值 param
  • 【Qt-11】http通信(Get同步收发)

    Qt 9 HTTP请求 post方式 WXG1011的博客 CSDN博客 QT 6 QWebApp服务器搭建及使用 qtwebapp WXG1011的博客 CSDN博客 写在前面 在上面两篇博文的基础上 继续迭代功能 上面两篇博客已实现QW
  • 谈Delphi编程中资源文件的应用

    一 初级应用篇 资源文件一般为扩展名为res的文件 在VC中资源文件用得非常普遍 但Delphi在其联机帮助中对资源文件没作什么介绍 其实利用其自带的资源编译工具BRCC32 EXE 一般位于 Delphi BIN目录下 我们完全可以做出跟
  • StringBuilder和StringBuffer&String的区别,以及它的基本用法

    StringBuilder在java5中引入 算的上是一个StringBuffer的一个用于单线程的版本 StringBuilder用于拼接字符串 用法跟StringBuffer差不多 都是创建一个字符缓存区 不用像String一样每增加一
  • JVM Tenured space is exhausted

    使用Android Studio命令gradlew assembleRelease打包apk 遇到了JVM Tenured space is exhausted的错误 网上查询后原因是JVM分配内存不足 需要在项目下的grade prope
  • 期货和股票的区别在哪里

    期货和股票的区别在哪里 股票与期货有什么区别 1 概念不同 股票是股份公司发行的所有权凭证 是各个股东作为持股凭证的一种有价证券 而期货属于一种标准化可交易合约 一种到期必须执行的合约 2 交易场所不同 股票是需要在证券交易所进行交易的 例
  • Netty实战(一)Nett的概念及体系结构

    Nett的概念及体系结构 第一章 Java网络编程 1 1 Java NIO 1 2 选择器 第二章 Netty是什么 2 1 Netty简介 2 2 Netty的特性 2 2 1 设计 2 2 2 易于使用 2 2 3 性能 2 2 4
  • 读取文件最后N行

    转自 http www zuidaima com share 1550463669226496 htm 指定行数 可以获取到从这行到文件尾的所有行 分享自大熊 源文件 读取最后10行结果 代码下载地址 http www zuidaima c
  • C++的智能指针unique_ptr、shared_ptr和weak_ptr

    C 的智能指针是一种特殊的指针类型 它能够自动管理内存资源 避免常见的内存泄漏和多次释放等问题 C 11引入了三种主要的智能指针 unique ptr shared ptr和weak ptr 目录 unique ptr shared ptr