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

news/2025/1/26 12:23:29/

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/news/1565965.html

相关文章

stm8s单片机(二)外部中断实验

中断优先级 stm8的中断优先级不是固定不变的&#xff0c;stm8的中断分为硬件优先级与软件优先级&#xff1b;当多个中断发生时&#xff0c;cpu会先响应软件优先级高的中断&#xff0c;若软件优先级相同会先响应硬件优先级高的&#xff1b; 其中软件优先级有四个 /*** brief …

我的2024年年度总结

序言 在前不久&#xff08;应该是上周&#xff09;的博客之星入围赛中铩羽而归了。虽然心中颇为不甘&#xff0c;觉得这一年兢兢业业&#xff0c;每天都在发文章&#xff0c;不应该是这样的结果&#xff08;连前300名都进不了&#xff09;。但人不能总抱怨&#xff0c;总要向前…

大模型GUI系列论文阅读 DAY4续:《Large Language Model Agent for Fake News Detection》

摘要 在当前的数字时代&#xff0c;在线平台上虚假信息的迅速传播对社会福祉、公众信任和民主进程构成了重大挑战&#xff0c;并影响着关键决策和公众舆论。为应对这些挑战&#xff0c;自动化假新闻检测机制的需求日益增长。 预训练的大型语言模型&#xff08;LLMs&#xff0…

QModbusTCPClient 服务器断开引起的程序崩溃

最近使用QModbusTCPClient 与一套设备通信&#xff0c;有一个QTimer频繁的通过读取设备寄存器。程序运行良好&#xff0c;但是有个问题&#xff1a;正常进行中设备断电了&#xff0c;整个程序都会崩溃。解决过程如下&#xff1a; 1.失败方案一 在QModbusTCPClient的errorOccu…

Leetcode2218:从栈中取出 K 个硬币的最大面值和

题目描述&#xff1a; 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币。 每一次操作中&#xff0c;你可以从任意一个栈的 顶部 取出 1 个硬币&#xff0c;从栈中移除它&#xff0c;并放入你的钱包里。 给你一个列表 piles &#xff0c;其中 piles[i] 是一…

2025/1/21 学习Vue的第四天

睡觉。 --------------------------------------------------------------------------------------------------------------------------------- 11.Object.defineProperty 1.在我们之前学习JS的时候&#xff0c;普通得定义一个对象与属性。 <!DOCTYPE html> <h…

Vue前端框架概述

Vue前端框架 Vue.js是一款用于构建用户界面的轻量级、渐进式JavaScript框架。它基于标准HTML、CSS和JavaScript构建&#xff0c;并提供了一套声明式的、组件化的编程模型。Vue以其简洁的语法、高效的性能以及强大的生态系统&#xff0c;在现代前端开发领域迅速崛起&#xff0c…

# [0114] Task01 《数学建模导论》P1 解析几何与方程模型

链接&#xff1a;https://www.datawhale.cn/activity/124 整理的相关代码库 GitHub 页面链接 绪论 姜启源&#xff1a;“数学建模就是建立数学模型解决实际问题” 本质还是解应用题&#xff0c;只是曾经的“小明买糖”变成了如今的“嫦娥探月”。 SEIR 模型&#xff0c;也…