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

devtools/2025/1/23 4:06:40/

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.ppmy.cn/devtools/152772.html

相关文章

1.3变革之力:Transformer 如何重塑深度学习的未来

变革之力:Transformer 如何重塑深度学习的未来 在深度学习的历史上,Transformer 是一项标志性的突破,彻底改变了自然语言处理(NLP)和机器学习领域的格局。自从它在2017年由 Vaswani 等人提出以来,Transformer 便成为了处理序列数据的标准架构,其强大的表达能力和计算效…

阿里云服务器在Ubuntu上安装redis并使用

1、redis安装 sudo apt install lsb-release curl gpgcurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpgecho "deb [signed-by/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.…

YOLOv9改进,YOLOv9检测头融合RFAConv卷积,适合目标检测、分割任务

摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…

SiamCAR(2019CVPR):用于视觉跟踪的Siamese全卷积分类和回归网络

原文标题:SiamCAR: Siamese Fully Convolutional Classification and Regression for Visual Tracking 中文标题:SiamCAR:用于视觉跟踪的Siamese全卷积分类和回归 代码地址: https://github.com/ohhhyeahhh/SiamCAR Abstract 通过将视觉跟踪任务分解为两个子问题,…

vim文本编辑器三种模式的转换关系

输入模式 ———— 末行模式 输入模式和末行模式不能相互转换。 输入模式 ———— 命令模式 输入模式可以通过点击esc进入命令模式。 命令模式可以通过点击i进入输入模式。 末行模式 ———— 命令模式 末行模式可以通过点击esc进入命令模式。 命令模式可以通过shift&…

基于STM32的智能门锁安防系统(开源)

目录 项目演示 项目概述 硬件组成&#xff1a; 功能实现 1. 开锁模式 1.1 按键密码开锁 1.2 门禁卡开锁 1.3 指纹开锁 2. 功能备注 3. 硬件模块工作流程 3.1 步进电机控制 3.2 蜂鸣器提示 3.3 OLED显示 3.4 指纹与卡片管理 项目源代码分析 1. 主程序流程 (main…

CSS 实体

如果希望在 HTML 中使用 CSS 显示以下任何字符&#xff0c;您可以使用下表中的 CSS 实体。 实例 <style> h1:after {content: \00A7; } </style> 所有 <h1> 元素都将在结尾显示该字符&#xff1a; <!DOCTYPE html> <html> <style> h1…

校园二手交易平台小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…