C++ 中的内存管理模式

2024-02-12

我认为我对正常(功能)设计模式有丰富的经验,例如所描述的在四人帮的书 http://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612,我主要用在java和C#中。在这些“托管”语言中,这几乎是完成工作所需了解的所有内容。

然而,在 C++ 世界中,开发人员还可以控制所有对象的分配、传递和删除方式。我理解原理(我读过斯特鲁斯特鲁普 https://rads.stackoverflow.com/amzn/click/com/0201889544等文本),但我仍然需要付出很大的努力来决定哪种机制最适合给定的场景 - 这就是与内存相关的设计模式组合有用的地方。

例如,昨天我必须创建一个类Results,它是一些对象的容器和另一种类型对象的集合(在本例中为 std::vector)。所以有一些我无法真正回答的设计问题:

  1. 我应该按值返回此类,还是按智能指针返回此类?
  2. 在类内部,向量和对象应该是普通成员,还是应该再次存储为智能指针?
  3. 在向量中,我应该直接存储对象,还是再次存储指向它们的智能指针?
  4. 我的 Results 类中定义的 getter 应该返回什么(即值、引用或智能指针)?

当然,智能指针很酷,但它们会造成语法混乱,而且我不相信对每个对象使用 malloc 是否是最佳方法。

我将不胜感激以上具体问题的答案,但更感激一些关于内存相关设计模式的更长、更一般的文本——这样我也可以解决周一遇到的问题!


您所有问题的答案最终都是相同的:这取决于您是否需要引用语义或值语义(和一些caveats予以考虑)。

如果你需要参考语义,这是 Java 和 C# 等语言中默认使用的 UDT(用户定义数据类型)声明的内容class关键字,那么你将不得不使用智能指针。在这种情况下,您希望多个客户端持有安全别名到一个特定的对象,其中这个词safe封装了这两个要求:

  1. 避免悬空引用,这样您就不会尝试访问不再存在的对象;
  2. 避免使用比所有对它们的引用都存活得更久的对象,这样就不会泄漏内存。

这是什么智能指针 do. 如果您需要参考语义(如果您的算法不会在需要共享所有权的情况下使引用计数的开销变得很大),那么你应该使用智能指针.

例如,当您希望同一对象成为多个集合的一部分时,您确实需要引用语义。当您更新一个集合中的对象时,您希望所有其他集合中同一对象的表示形式得到一致更新。在这种情况下,您可以在这些集合中存储指向对象的智能指针。智能指针封装了identity对象的性质而不是它的价值。

但如果你不需要创建别名,那么值语义可能是你应该依赖的。这是在 C++ 中声明具有自动存储功能(即在堆栈上)的对象时默认得到的结果。

需要考虑的一件事是 STL 集合存储values,所以如果你有一个vector<T>, then copies of T将存储在您的vector。始终假设您不需要引用语义,如果您的对象很大并且复制成本很高,那么这可能会成为一种开销。

为了限制这种情况的可能性,C++11 附带了move操作,这使得当不再需要对象的旧副本时可以有效地按值传输对象。


我现在将尝试使用上述概念回答你的问题更直接。

1) 我应该按值返回此类,还是按智能指针返回此类?

这取决于您是否需要引用语义。该函数对该对象执行什么操作?该函数返回的对象是否应该由许多客户端共享?如果是这样,则通过智能指针。如果不是,是否可以定义一个有效的移动操作(几乎总是这样)?如果是这样,那么按价值。如果没有,则通过智能指针。

2) 在类内部,向量和对象应该是普通成员,还是应该再次存储为智能指针?

最有可能作为普通成员,因为向量通常在概念上是对象的一部分,因此它们的生命周期与嵌入它们的对象的生命周期绑定。在这种情况下,您很少需要引用语义,但如果需要,请使用智能指针。

3) 在向量中,我应该直接存储对象,还是再次存储指向它们的智能指针?

与第 1 点的答案相同:您需要共享这些对象吗?您应该存储这些对象的别名吗?您是否希望在引用这些对象的代码的不同部分中看到对这些对象的更改?如果是这样,则使用共享指针。如果没有,是否可以有效地复制和/或移动这些对象?如果是这样(大多数时候),请存储值。如果没有,则存储智能指针。

4) 我的 Results 类中定义的 getter 应该返回什么(即值、引用或智能指针)?

与第 2 点的答案相同:这取决于您计划如何处理返回的对象:您是否希望它们被代码的许多部分共享?如果是,则返回一个智能指针。如果它们仅由一个部分独占,则按值返回,除非移动/复制这些对象成本太高或根本不允许(不太可能)。在这种情况下,返回一个智能指针。


作为旁注,请注意,C++ 中的智能指针比 Java/C# 引用有点棘手:首先,智能指针有两种主要风格,具体取决于是否共享所有权 (shared_ptr) or 独特的所有权 (unique_ptr)是所期望的。其次,您需要避免循环引用shared_ptr,这将创建对象岛,即使您正在运行的代码无法再访问它们,它们也可以使彼此保持活动状态。这就是为什么弱指针 (weak_ptr) exist.

这些概念自然而然地引出了以下概念:责任用于管理对象的生命周期或(更一般地)管理已使用的资源。您可能想阅读有关 RAII 习惯用法的信息,例如(资源获取即初始化),以及一般的异常处理(编写异常安全代码是这些技术存在的主要原因之一)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中的内存管理模式 的相关文章

随机推荐

  • Julia:在函数内解压参数的最佳实践

    我可以解压一个元组 我正在尝试编写一个函数 或宏 它可以从类型构造函数的实例中解压其中的子集Parameters 也就是说 我知道该怎么做 a b c unpack p Parameters 但我想做这样的事情 b c unpack p P
  • Java8 IntStream 与 Collections.stream() 不兼容的返回类型

    我对此有点迷失 我有代码 我没有编写 其中有一个名为 BitSetExt 的类 它扩展了 BitSet 签名看起来像 private class BitSetExt extends BitSet implements Set
  • 警告:逗号表达式的左侧操作数无效

    我看到这个警告 如何修复它 Temp Table i temp3 MSB i 0x1 警告 逗号表达式的左侧操作数无效 什么是表对象 如果是二维数组那么你应该写 Table i temp3 在你的代码中 i temp3 是一个逗号运算符 它
  • Knockout.js 项目结构

    我一直将 Angular js 和 Knockout js 视为潜在的前端解决方案 我喜欢 Knockout 的教程和文档 但是 我不清楚 Knockout 如何处理大型应用程序的模板化 例如 在 Angular 中 您可以创建一个如下所示
  • Google Analytics gtag.js 手动将链接器跨域参数添加到 URL

    对于 Google Analytics 分析 的 gtag js 实现 希望手动将链接器参数添加到特定 URL 以便 ga 参数出现在单击的 URL 上 但文档似乎位于https developers google com analytic
  • MFMailComposeViewController 将签名附加为 .txt 文件

    我正在通过 MFMailComposeViewController 发送 pdf 在 Outlook 中查看时 发件人的签名被视为附件 但在 iOS 本机邮件应用程序中则不然 其他一切都按预期工作 签名显示附件的原因是什么 IBAction
  • 限制事件监听器每秒可以触发的次数

    我正在使用 Gamepad API 特别是使用控制器上的操纵杆的轴 这些位置的更新很多而且经常 因此 我正在监听的事件 棍子上的运动 也经常发生 有什么方法可以将其发生次数限制为每秒 25 次 以减少延迟吗 您无法限制 JavaScript
  • 如何用文本+图像动态填充Android Spinner

    这就是我想要实现的目标我能够实现这么多 我能够将文本和图像添加到微调器中 但图像没有显示在文本旁边的下拉菜单中 它仅针对微调器的选定项目显示 这是我的代码 spinner layout xml包含主微调器的布局
  • Slickgrid 复选框和过滤问题

    我有一个填充有数据的 slickGrid 并通过以下方式添加了第一个复选框列 if info includeSelectCheckbox var checkboxSelector new Slick CheckboxSelectColumn
  • Tkinter Checkbutton 和事件回调函数

    这是我在 Effbot 网站上找到的一个小代码示例 它与我想要在我的一个程序中执行的操作很接近 from Tkinter import fen Tk class test Tk class def init self self var In
  • BigDecimal 的 StringBuilder 的模拟

    我有一个 BigDecimals 列表需要求和 如果它们是要连接的字符串 我会使用 StringBuilder 来减少对象创建 BigDecimal 有类似的东西吗 或者也许我不应该为此烦恼 BigDecimal 创建的优化值得花精力吗 B
  • pgadmin4 无法在 nginx 后面的特定位置工作

    我遇到了一些麻烦 pgadmin 在位置 中的 nginx 后面工作完美 但它不会在位置 pgadmin 后面工作 工作很棒 location proxy http version 1 1 proxy set header X Real I
  • 计算小距离用什么公式

    Hy 我需要计算 2 个 GPS 点之间的距离 我读了这个问题计算地理邻近度的公式 https stackoverflow com questions 2096385 formulas to calculate geo proximity但
  • 更改 MouseOver 上的 MenuItem 的颜色

    我想在鼠标悬停时更改菜单项的颜色 我还需要圆形边框 图像和文本框 当我设置样式时 一切正常 只有 mouseOverEvent 正在执行任何操作 背景不会改变 我的代码是
  • 绕圆形路径移动一个点[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个带有二维坐标的点 我需要更改点坐标值才能遵循圆形路径 我将如何使用 C 实现它 使用正弦和余弦 for double t 0
  • 有没有办法让字典作为 python 中 pandas Dataframe 的条目?

    就像是 d a 1 b 2 data pandas DataFrame data new column d data new column 0 最后一个命令将返回字典 d 您可以将字典包装在列表中 以便将字典视为元素而不是可迭代对象 d a
  • C++ 向量元素计数

    在C 中 使用向量头 如何找到元素的数量 include
  • 是否可以修复长 html huxtable 的头部?

    这适用于使用以下命令生成的表kableExtra这在检查长桌子时很有用 我想知道是否有人知道如何做到这一点huxtable 这是一个示例 Rmd 以了解我的意思 我希望在向下滚动时将 huxtable 标题固定在页面顶部 就像在 kable
  • 短路和一元运算符的工作

    请看一下下面的代码 int i 5 boolean b i lt 5 i lt 5 line 2 System out println i line 3 prints 5 在第2行 根据我的理解 因为在所有运算符中 具有最高优先级 i应首先
  • C++ 中的内存管理模式

    我认为我对正常 功能 设计模式有丰富的经验 例如所描述的在四人帮的书 http www amazon co uk Design patterns elements reusable object oriented dp 0201633612