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

C++ 智能指针(八股总结)

C++中的智能指针有哪些,各自有什么作用?

智能指针主要解决一个内存泄露的问题,它可以自动地释放内存空间。因为它本身是一个类,当函数结束的时候会调用析构函数,并由析构函数释放内存空间。智能指针分为共享指针(shared_ptr), 独占指针(unique_ptr)和弱指针(weak_ptr):

(1)shared_ptr ,多个共享指针可以指向相同的对象,采用了引用计数的机制,当最后一个引用销毁时,释放内存空间;

(2)unique_ptr,保证同一时间段内只有一个智能指针能指向该对象(可通过move操作来传递unique_ptr);

(3)weak_ptr,用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

std::unique_ptr

定义

std::unique_ptr 是独占所有权的智能指针,一个对象只能被一个std::unique_ptr管理,不能共享所有权。

主要用途

  1. 自动释放资源:当std::unique_ptr超出作用域时,会自动释放所管理的资源。
  2. 独占所有权:确保同一资源不会被多个指针操作,避免潜在的内存问题。
  3. 高效传递所有权:通过std::move可以将资源所有权转移。

示例代码

#include <iostream>
#include <memory>

void uniquePtrExample() {
    std::unique_ptr<int> uptr1 = std::make_unique<int>(10); // 创建并管理资源
    std::cout << "Value: " << *uptr1 << std::endl;

    // std::unique_ptr<int> uptr2 = uptr1; // 错误!不能复制
    std::unique_ptr<int> uptr2 = std::move(uptr1); // 转移所有权
    if (!uptr1) {
        std::cout << "uptr1 is now empty." << std::endl;
    }
    std::cout << "Value from uptr2: " << *uptr2 << std::endl;
}

int main() {
    uniquePtrExample();
    return 0;
}

输出

Value: 10
uptr1 is now empty.
Value from uptr2: 10

std::shared_ptr

定义

std::shared_ptr 是具有共享所有权的智能指针,多个std::shared_ptr可以同时管理同一资源,资源会在最后一个std::shared_ptr销毁时释放。

主要用途

  1. 共享资源的生命周期管理:适用于多个对象需要访问和管理同一资源的场景。
  2. 引用计数:内部维护一个引用计数,记录资源被引用的次数。
  3. 线程安全:引用计数是线程安全的。

示例代码

#include <iostream>
#include <memory>

void sharedPtrExample() {
    std::shared_ptr<int> sptr1 = std::make_shared<int>(20); // 创建并管理资源
    std::shared_ptr<int> sptr2 = sptr1; // 共享资源
    std::cout << "Value: " << *sptr1 << ", Use count: " << sptr1.use_count() << std::endl;

    sptr2.reset(); // sptr2释放共享的资源
    std::cout << "After reset, Use count: " << sptr1.use_count() << std::endl;
}

int main() {
    sharedPtrExample();
    return 0;
}

输出

Value: 20, Use count: 2
After reset, Use count: 1

std::weak_ptr

定义

std::weak_ptr 是一种弱引用的智能指针,它不会增加资源的引用计数,通常与std::shared_ptr配合使用,解决循环引用问题。

主要用途

  1. 解决循环引用问题:避免std::shared_ptr之间的循环引用导致资源无法释放。
  2. 观察资源状态:可以安全地观察std::shared_ptr管理的资源是否仍然存在。

示例代码

#include <iostream>
#include <memory>

void weakPtrExample() {
    std::shared_ptr<int> sptr = std::make_shared<int>(30);
    std::weak_ptr<int> wptr = sptr; // 创建弱引用

    std::cout << "Use count: " << sptr.use_count() << std::endl;
    if (auto sp = wptr.lock()) { // 检查资源是否有效
        std::cout << "Value: " << *sp << std::endl;
    }

    sptr.reset(); // 释放资源
    if (wptr.expired()) {
        std::cout << "Resource has been released." << std::endl;
    }
}

int main() {
    weakPtrExample();
    return 0;
}

输出

Use count: 1
Value: 30
Resource has been released.

区别分析
特性std::unique_ptrstd::shared_ptrstd::weak_ptr
所有权独占所有权共享所有权无所有权,仅观察资源
引用计数
典型用途独占资源管理共享资源管理解决循环引用问题
转移操作通过std::move支持复制和共享不直接管理资源

shared_ptr的实现原理是什么?构造函数、拷贝构造函数和赋值运算符怎么写?shared_ptr是不是线程安全的?

(1)shared_ptr是通过引用计数机制实现的,引用计数存储着有几个shared_ptr指向相同的对象,当引用计数下降至0时就会自动销毁这个对象;

(2)具体实现:

1)构造函数:将指针指向该对象,引用计数置为1;

2)拷贝构造函数:将指针指向该对象,引用计数++;

3)赋值运算符:=号左边的shared_ptr的引用计数-1,右边的shared_ptr的引用计数+1,如果左边的引用技术降为0,还要销毁shared_ptr指向对象,释放内存空间。

(3)shared_ptr的引用计数本身是安全且无锁的,但是它指向的对象的读写则不是,因此可以说shared_ptr不是线程安全的。[shared_ptr是线程安全的吗? - 云+社区 - 腾讯云 (tencent.com)](

shared_ptr 的循环引用
定义

shared_ptr 的循环引用(circular reference)是指两个或多个 shared_ptr 实例通过相互引用彼此共享的资源,导致引用计数无法归零,从而无法释放资源的问题。

问题原因
  • shared_ptr 通过引用计数来管理资源。
  • 如果两个 shared_ptr 互相引用,计数器永远不会归零,资源也就无法释放,造成 内存泄漏

示例代码

以下是一个简单的循环引用示例:

#include <iostream>
#include <memory>

class Node {
public:
    std::shared_ptr<Node> next;
    ~Node() { std::cout << "Node destroyed\n"; }
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();

    // 创建循环引用
    node1->next = node2;
    node2->next = node1;

    // 离开作用域,理论上 node1 和 node2 应该被销毁
    return 0;
}

// 输出:
// (没有任何 "Node destroyed" 的输出)

在上述代码中:

  1. node1node2 各自的 shared_ptr 引用计数为 1。
  2. 它们通过 next 成员指针互相引用,导致 shared_ptr 的引用计数始终为 1。
  3. 离开作用域时,引用计数无法归零,析构函数不会被调用,资源泄漏。

解决方案:使用 weak_ptr

为了避免循环引用,可以使用 weak_ptr 替代其中一个 shared_ptr,因为 weak_ptr 不会增加引用计数。

修正后的代码
#include <iostream>
#include <memory>

class Node {
public:
    std::weak_ptr<Node> next; // 使用 weak_ptr 打破循环
    ~Node() { std::cout << "Node destroyed\n"; }
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->next = node1; // 使用 weak_ptr 不会增加引用计数

    return 0;
}

// 输出:
// Node destroyed
// Node destroyed

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

相关文章:

  • 【Python项目】小区监控图像拼接系统
  • Tcl教程
  • 基于python的博客系统设计与实现
  • MFC 使用 32位带Alpha通道的位图
  • Visual Studio Community 2022(VS2022)安装方法
  • 题解 CodeForces 1037D Valid BFS? 三种解法 C++
  • 【组件库】使用Vue2+AntV X6+ElementUI 实现拖拽配置自定义vue节点
  • Springboot sse 示例
  • (done) 并行计算学习 (Day1: 两个简单的 OpenMP 例子)
  • JavaWeb开发(十五)实战-生鲜后台管理系统(二)注册、登录、记住密码
  • 【C++】揭秘类与对象的内在机制(核心卷之深浅拷贝与拷贝构造函数的奥秘)
  • 《从入门到精通:蓝桥杯编程大赛知识点全攻略》(五)-数的三次方根、机器人跳跃问题、四平方和
  • Python 进阶 - Excel 基本操作
  • 智能系统的感知和决策
  • 第15篇:从入门到精通:Python标准库详解
  • LeetCode 热题 100_全排列(55_46_中等_C++)(递归(回溯))
  • 简识JVM私有内存区域栈、数据结构
  • 蓝桥杯R格式--高精度算法模拟
  • 【MySQL】 常见数据类型
  • 10倍数据交付提升 | 通过逻辑数据仓库和数据编织高效管理和利用大数据
  • C#程序关闭时保证所有线程结束的方法
  • elasticsearch 数据导出/导入
  • 【记录】记录项目中的问题
  • Linux常用汇总
  • windows下修改docker的镜像存储地址
  • 易语言模拟真人鼠标轨迹算法 - 防止游戏检测