C++一分钟之-智能指针:unique_ptr与shared_ptr

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 【6月更文挑战第24天】C++智能指针`unique_ptr`和`shared_ptr`管理内存,防止泄漏。`unique_ptr`独占资源,离开作用域自动释放;`shared_ptr`通过引用计数共享所有权,最后一个副本销毁时释放资源。常见问题包括`unique_ptr`复制、`shared_ptr`循环引用和裸指针转换。避免这些问题需使用移动语义、`weak_ptr`和明智转换裸指针。示例展示了如何使用它们管理资源。正确使用能提升代码安全性和效率。

智能指针是C++中用于自动管理内存的工具,它们通过模拟拥有所有权的对象来防止内存泄漏,其中unique_ptrshared_ptr是最常用的两种类型。本文将深入探讨这两种智能指针的工作原理、应用场景、常见问题、易错点及避免策略,并通过具体代码示例加以说明。
image.png

unique_ptr与shared_ptr概览

unique_ptr

unique_ptr表示独占所有权的智能指针,同一时间内只能有一个unique_ptr指向给定的资源。当unique_ptr离开作用域时,它所管理的资源会被自动释放。这种设计保证了资源的唯一性和确定性释放。

shared_ptr

shared_ptr允许多个智能指针共享同一个资源的所有权。它通过引用计数来追踪有多少个shared_ptr指向同一资源,当最后一个指向该资源的shared_ptr销毁时,资源被释放。这使得shared_ptr非常适合于复杂数据结构的共享和跨组件传递。

常见问题与易错点

误用unique_ptr共享资源

尝试复制unique_ptr会导致编译错误,因为它是独占所有权的。试图通过值传递或赋值方式分享unique_ptr管理的资源是错误的。

循环引用导致的内存泄漏

使用shared_ptr时,如果不小心形成了循环引用(两个或多个shared_ptr互相引用形成闭环),即使所有指向它们的普通引用都已消失,它们的引用计数也不会降为零,从而导致资源无法释放,引发内存泄漏。

忽略裸指针转换

从原始指针到智能指针的转换需谨慎,特别是当原始指针已被其他地方管理时,直接构造智能指针可能会导致重复释放资源。

如何避免这些问题

使用转移语义避免unique_ptr误用

利用unique_ptr的移动语义(move semantics),而非拷贝,来传递资源的所有权。

破坏循环引用

  • 使用weak_ptr:当不需要增加引用计数时,使用weak_ptr来监视shared_ptr而不增加其引用计数,可以打破潜在的循环引用。
  • 重新设计数据结构:避免不必要的相互引用,或使用其他设计模式(如观察者模式)来替代直接的相互持有。

明智地转换裸指针

  • 在将裸指针转换为智能指针之前,确保该指针未被其他智能指针管理。
  • 使用make_shared来创建shared_ptr,以减少潜在的内存分配次数和提高效率。

代码示例

unique_ptr示例

#include <memory>

void manageResource(std::unique_ptr<int> ptr) {
   
   
    // 使用资源
} // ptr在此处自动销毁,资源被释放

int main() {
   
   
    auto ptr = std::make_unique<int>(42); // 创建并初始化unique_ptr
    manageResource(std::move(ptr)); // 移动所有权到函数内
    // ptr现在为空,资源已在manageResource内部被释放
    return 0;
}

shared_ptr与weak_ptr示例

#include <memory>

class Node {
   
   
public:
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;
    // ...其他成员和方法
};

void createChain() {
   
   
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->prev = node1; // 使用weak_ptr避免循环引用
}

int main() {
   
   
    createChain();
    // 所有资源在离开作用域时将被正确释放,无内存泄漏风险
    return 0;
}

总结

unique_ptrshared_ptr是C++智能指针家族中的两大支柱,它们各自适用于不同的场景。正确使用它们不仅能够有效避免内存泄漏,还能简化资源管理,提升代码的安全性和可维护性。通过了解它们的工作原理、识别常见问题和易错点,并采取相应的避免策略,开发者可以更加高效地利用智能指针的强大功能,构建高质量的C++应用程序。

目录
相关文章
|
8月前
|
缓存 安全 编译器
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
《C++面试冲刺周刊》第三期聚焦指针与引用的区别,从青铜到王者级别面试回答解析,助你21天系统备战,直击高频考点,提升实战能力,轻松应对大厂C++面试。
809 132
C++面试周刊(3):面试不慌,这样回答指针与引用,青铜秒变王者
|
8月前
|
存储 C++
C++语言中指针变量int和取值操作ptr详细说明。
总结起来,在 C++ 中正确理解和运用 int 类型地址及其相关取值、设定等操纵至关重要且基础性强:定义 int 类型 pointer 需加星号;初始化 pointer 需配合 & 取址;读写 pointer 执向之处需配合 * 解引用操纵进行。
684 12
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
773 4
|
存储 安全 编译器
在 C++中,引用和指针的区别
在C++中,引用和指针都是用于间接访问对象的工具,但它们有显著区别。引用是对象的别名,必须在定义时初始化且不可重新绑定;指针是一个变量,可以指向不同对象,也可为空。引用更安全,指针更灵活。
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
275 2
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
643 1
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
473 12
|
10月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
254 0