C++ 中的所有权语义2025 年 5 月 13 日 | 阅读 7 分钟 C++ 中的所有权语义是定义内存和文件句柄等资源如何管理的基础概念。所有权确实对这些资源的生命周期有直接影响,这对于确保没有内存泄漏和最大限度地减少运行时错误的可能性至关重要。自 C++11 引入智能指针和移动语义以来,所有权已成为现代 C++ 资源管理的一个支柱。本章将深入探讨这些方面,以建立对 C++ 中所有权的全面理解。 所有权可分为两类
RAII 和所有权资源获取初始化 (RAII) 是体现所有权概念的核心 C++ 习惯用法。在 RAII 中,资源分配和释放与对象的生命周期相关联。C++ 通过确保资源在构造函数中获取,并在析构函数中释放,来强制执行可预测的所有权模式。 例如 在这种方法中,资源类本身处理其资源的生命周期。当资源类的对象创建时,资源将被获取,当它超出范围时,它将自动释放资源,即 RAII 原则。 原始指针和所有权问题在 C++ 的早期版本中,所有权通常是通过原始指针来处理的。尽管原始指针简单灵活,但它们不会明确指示所有权,因此很容易导致双重删除、内存泄漏和悬空指针等问题。 例如,考虑以下代码: 代码可以工作,但如果我们忘记删除 ptr,就会导致内存泄漏。同样,如果我们删除两次,就会导致未定义行为。后者已通过 C++11 标准引入的智能指针得以解决。 智能指针和所有权智能指针类专门用于自动化动态分配内存的管理。它们封装了原始指针,并负责在不再需要时解除分配内存,从而更有效地管理所有权。 std::unique_ptr 的独占所有权std::unique_ptr 是资源的独占所有者。在任何给定时间,只有一个 std::unique_ptr 实例可以拥有一个资源,这强制执行了清晰的所有权语义,并且当 std::unique_ptr 超出范围时,资源会被自动删除。 std::unique_ptr 不能被复制,只能被移动,这强化了其独占所有权 移动后,ptr1 会失去资源的拥有权并被设置为 nullptr。 std::shared_ptr 的共享所有权当需要共享资源的所有权时,可以使用 std::shared_ptr 函数。多个 std::shared_ptr 可以拥有一个资源,并且当最后一个 std::shared_ptr 超出范围时,资源将被自动释放。 在下面的情况下,sharedPtr1 和 sharedPtr2 共享整数的所有权。当 sharedPtr1 和 sharedPtr2 都被销毁时,资源将被释放。 std::weak_ptr 的弱所有权std::weak_ptr 是一个非拥有引用,它可以观察但不拥有资源。它通常用于在共享所有权可能导致内存泄漏时打破循环引用。std::weak_ptr 不会增加 std::shared_ptr 的引用计数。 如果 sharedPtr 被销毁,资源将被释放,weakPtr 将不再有效。std::weak_ptr 函数还提供了一个 lock 函数,通过提供一个临时的 std::shared_ptr,使访问资源变得安全。 移动语义和所有权转移自 C++11 起,移动语义通过允许“移动”而不是深层复制资源,实现了高效的所有权转移。移动通过右值引用 (&&) 实现。 移动 std::unique_ptrstd::unique_ptr 函数是一个很好的例子,因为它强制执行独占所有权;std::unique_ptr 对象只能通过移动而不能通过复制来转移。 移动后,ptr1 被设置为 nullptr,因为它的所有权已转移到 ptr2。 移动构造函数和移动赋值运算符管理资源的类通常定义一个移动构造函数和一个移动赋值运算符,以实现安全的所有权转移。以下是一个具有移动语义的自定义类的示例: 通过移动语义,我们可以安全地转移 MyClass 中资源的拥有权,而无需进行昂贵的复制。 所有权和容器所有权语义适用于 C++ 容器,如 std::vector 和 std::list。如果对象被添加到容器中,则适用所有权。容器会创建所包含对象的副本,除非它们包含智能指针或已被显式移动。 例如 这表明在上面的示例中,std::unique_ptr 已被移动到 vector 中,从而将所有权转移给了容器。 常见的所有权场景
示例让我们举一个例子来说明 C++ 中的所有权语义。 输出 ![]() 高级所有权模式和最佳实践std::unique_ptr、std::shared_ptr 和 std::weak_ptr 的所有权语义可用于更复杂的高级资源管理模式,同时牢记它们的基本用法。 1. 自定义删除器:智能指针还支持指定自定义删除器。这对于内存以外的资源管理操作非常有用。例如,std::unique_ptr 函数还可以采用自定义删除器来管理文件句柄、套接字或任何需要不同清理例程的其他类型的资源。 2. Pimpl 习惯用法:使用 std::unique_ptr 将类的实现细节隐藏在 .cpp 文件中,从而减少依赖性,甚至加快编译速度。该习惯用法强制执行清晰的所有权,并将实现细节保留在一个私有位置。 3. 基于容器的仅移动类型:C++ 应用程序开发人员现在可以创建旨在不可复制但可移动的类,通过所有权语义,这会删除复制构造函数和赋值运算符。现在,在容器中使用仅移动类型将是高效的,因为像 std::vector 这样的容器将能够在不进行不必要复制的情况下转移所有权,从而限制资源消耗。 这些程序允许高性能、内存安全的 C++ 作者保留对资源的严格控制。因此,所有权语义是现代 C++ 编程的重要组成部分,它允许开发人员充分利用 RAII 和自动资源管理的强大功能。 多线程环境下的所有权所有权语义在多线程应用程序中非常重要,尤其是在共享资源和并发访问资源时。在这种情况下,当多个线程共享资源时,通常会应用 std::shared_ptr 来进行安全的引用计数。这样,只要至少有一个线程需要该资源,该资源就可以保持有效。但是,如果所有线程同时修改资源,共享所有权很容易导致竞态条件。 这些情况通常使用 std::shared_ptr 和 std::mutex 或 std::atomic 来实现,以防止数据争用并确保线程安全。另一方面,std::weak_ptr 函数可用于安全地观察共享资源,而不会阻止其释放,这在访问仅是暂时的且不影响所有权生命周期的情况下很有用。 通过结合这些技术,开发人员可以实现健壮、线程安全的所有权管理,在并发应用程序中平衡效率和安全性。 |
这是 <random> 库的一部分,用于模拟 Student's t 分布。在假设检验中经常使用它,因为样本数量通常较小,并且总体方差未知。t 分布,通常称为 Student's t 分布,是……
阅读 4 分钟
引言:零和博弈论中的一种博弈,其中一个玩家的损失将等于另一个玩家的收益。它对于竞争的设定至关重要,其中由对手的战略行为决定。在经济学中,...
7 分钟阅读
简介 C++ STL 中的 UTF-8 到宽字符转换是现代软件开发中的一项基本任务,特别是在多语言支持和国际化至关重要的环境中。UTF-8(Unicode 转换格式 - 8 位)因其...已成为编码 Unicode 字符的事实标准。
5 分钟阅读
在本文中,我们将讨论在 C++ 中遇到数字时如何反转字符串。问题陈述问题是在字符串中每当遇到数字时反转字符串的片段。换句话说,由数字之间的字符组成的每个片段都应该...
阅读 4 分钟
在本主题中,我们将讨论 C++ 编程语言中的基于范围的 for 循环。C++ 语言在 C++11 及更高版本中引入了一种新的基于范围的 for 循环概念,它比常规的 For 循环要好得多。基于范围的 for 循环做...
5 分钟阅读
八十边形数组由具有 80 条边的多边形的形数组成。八十边形数属于此类多边形的系列,即三角形、正方形等。这些数字中的数学和视觉模式也可以通过...进行解释。
7 分钟阅读
在本文中,我们将讨论 C++ 中的 std::has_facet() 方法及其语法、参数和示例。std::has_facet() 方法是什么?C++ 中的 std::has_facet 函数是一个实用函数,用于确定给定区域设置中是否存在某个特定区域设置组件。区域设置组件是必不可少的组成部分……
阅读 4 分钟
Solovay-Strassen 素数测试是一种概率算法,用于确定给定的数字是素数还是合数。与保证素性但对于大数字来说计算成本高昂的埃拉托斯特尼筛法等确定性方法不同,Solovay-Strassen 平衡了效率和准确性。该算法的核心是...
阅读 4 分钟
C++ 标准库提供了各种流类,便于格式化的输入和输出操作。C++20 中较新的一个添加项是 `std::basic_ospanstream`。它是 `
阅读 4 分钟
跳表是一种数据结构,它提供了一种在排序序列中高效地搜索、插入和删除元素的方法。它是由 William Pugh 在 1989 年发明的,作为平衡树的一种替代方案,具有相似的平均情况性能特征,但实现更简单。问题...
阅读 12 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India