c++ - C++中的内存管理模式

标签 c++ design-patterns memory-management

我认为我对标准(功能性)设计图案有相当的经验,例如在the gang of four book中,我主要在java和C#中使用它。在这些“托管”语言中,这是完成工作所需的几乎所有知识。

但是,在C++世界中,开发人员还可以控制如何分配,传递和删除所有对象。我了解这些原理(在其他课本中我读了Stroutrup),但是仍然需要大量的精力来决定哪种机制最适合给定的场景-这是与内存相关的设计模式组合的有用之处。

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

  • 我应该按值还是按智能指针返回此类?
  • 在类内部, vector 和对象应该是普通成员,还是应该再次将它们存储为智能指针?
  • 在 vector 中,我应该直接存储对象,还是再次存储指向它们的智能指针?
  • 在我的Results类上定义的 setter/getter 应返回什么(即值,引用或智能指针)?

  • 当然,智能指针很酷,但不是,但是它们会产生语法困惑,并且我不认为对每个对象使用malloc是否是最佳方法。

    对于上述特定问题,我将不胜感激,对于与内存相关的设计模式的更长,更通用的文章,我将不胜感激-这样我也可以解决周一将遇到的问题!

    最佳答案

    您所有问题的答案最终都是相同的:,这取决于您是否需要引用语义或值语义(需要注意一些注意事项)。

    如果您需要引用语义,这是默认情况下在Java和C#语言中使用class关键字声明的UDT(用户定义的数据类型)所具有的语言,那么您将必须使用智能指针。在这种情况下,您希望多个客户端为特定对象保留安全别名,其中“安全”一词封装了以下两个要求:

  • 避免悬挂引用,这样您就不会尝试访问不再存在的对象。
  • 避免使用所有对它们的引用都没有生命的对象,以免泄漏内存。

  • 这就是智能指针的功能。如果您需要引用语义(并且如果您的算法不能使需要共享所有权的地方进行引用计数的开销很大),那么应该使用智能指针。

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

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

    要考虑的一件事是STL集合存储值,因此,如果您有vector<T>,那么T的副本将存储在vector中。始终假设您不需要引用语义,如果您的对象很大且复制昂贵,那么无论如何这可能会成为开销。

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

    现在,我将尝试使用以上概念来更直接地回答您的问题

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

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

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

    由于 vector 在概念上通常是对象的一部分,因此很可能是普通成员,因此 vector 的生命周期与嵌入 vector 的对象的生命周期有关。在这种情况下,您很少需要引用语义,但是如果这样做,则使用智能指针。

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

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

    4)在我的Results类上定义的 setter/getter 应返回什么(即值,引用或智能指针)?

    与第2点相同的答案:这取决于您打算对返回的对象执行什么操作:您是否希望代码的许多部分共享它们?如果是这样,则返回一个智能指针。如果它们仅由一部分独占,则按值(value)返回,除非移动/复制这些对象太昂贵或根本不允许(非常不可能)。在这种情况下,返回一个智能指针。

    附带说明一下,请注意,C++中的智能指针比Java/C#引用要复杂一些:首先,根据共享所有权(shared_ptr)或唯一所有权(unique_ptr)是否具有两种主要的智能指针风格想要的。其次,您需要避免循环引用shared_ptr,这将创建对象孤岛,即使您的运行代码无法再访问它们,它们也可以使彼此保持 Activity 状态。这就是为什么存在弱指针(weak_ptr)的原因。

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

    关于c++ - C++中的内存管理模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14539624/

    相关文章:

    c++ - 配置 : error: C++ compiler cannot create executables

    java - 提取特定行java

    objective-c - 泄漏是如何发生的?

    c# - finally block 的实际使用

    c++ - 友元函数未在此范围内声明错误

    c++ - 初始化前使用的静态 map

    c++ - 如何遍历 C++ 中的无序集?

    c++ - 还是观察者模式?

    algorithm - 使用自己的确认对话框实现验证链?

    c - 不同硬件架构下的不同内存泄漏