C ++类成员:堆栈与堆分配

时间:2018-09-24 19:17:34

标签: c++ heap

考虑像这样的类

class Element{
    public:
    // approx. size of ~ 2000 Byte
    BigStruct aLargeMember;

    // some method, which does not depend on aLargeMember
    void someMethod();
}

现在假定在运行时期间创建了Element的许多实例(例如,运行时为100,000,000个实例,同时存在大约50,000个实例),并且通常仅调用someMethod(),而无需分配aLargeMember的内存。 (此说明性示例是从非线性有限元代码派生的,类Element实际上表示有限元。)

现在我的问题是:由于aLargeMember并不是经常需要的,并且考虑到Element的大量实例,动态创建aLargeMember是否有利?例如

class Element{
    public:
    // approx. size of ~ 2000 Byte
    std::unique_ptr<BigStruct> aLargeMember;

    // only called when aLargeMember is needed
    void initializeALargeMember{
        aLargeMember = std::unique_ptr<BigStruct>( new BigStruct() );}

    // some method, which does not depend on aLargeMember
    void someMethod();
}

基本上,这对应于https://stackoverflow.com/a/36646563/4859499中给出的建议4:

  

仅在有明确需求时才使用new,例如:

     
      
  • 一个特别大的分配,它将占用大量堆栈(您的OS /进程将“协商”一个限制,通常在1-8 MB以上的范围内)

         
        
    • 如果这是您使用动态分配的唯一原因,并且您确实希望将对象的生存期绑定到函数中的作用域,   您应该使用本地std :: unique_ptr <>来管理动态   内存,并确保无论您如何离开示波器都将其释放:   通过返回,抛出,中断等。(您也可以使用std :: unique_ptr <>   类/结构中的数据成员,以管理对象拥有的任何内存。)
    •   
  •   

因此,我的问题是:在当前情况下,堆方法是否被视为不良做法?还是在当前情况下对堆有任何好的论点?预先谢谢你!

1 个答案:

答案 0 :(得分:3)

  

C ++类成员:堆栈与堆分配

不要将基于堆栈的分配与为堆分配对象的成员变量混淆。如果您有此类的堆分配对象:

class Element{
    public:
    // approx. size of ~ 2000 Byte
    BigStruct aLargeMember;

    // some method, which does not depend on aLargeMember
    void someMethod();
}

然后在这里没有任何内容分配到堆栈

  

由于不经常需要aLargeMember,因此请考虑   大量的Element实例,对   动态创建aLargeMember?

您所描述的场景确实是动态内存分配的合理候选者,因为如果不这样做,只会使您分配的方式超出您的需求,没有好处-因此您没有太多其他选择。

  

在当前情况下,堆方法是否被视为不良做法?

这个问题有点太笼统了,很容易解释为基于观点,但是在给定的代码段中,您似乎指的是根据需要分配成员变量的场景-basis,因此最好使用la lazy initialization,以便减轻必须手动维护代码中要求的每个点的初始化的开销。为此,您可以包装对此成员的访问权限以确保您返回已初始化的内容。仅出于说明目的,不是很线程安全,它的思路大致如下:

class Element{
private:
    // approx. size of ~ 2000 Byte
    std::unique_ptr<BigStruct> aLargeMember;

    // A wrapper through which you access aLargeMember
    BigStruct& GetLargeMember()
    {
        if (!aLargeMember)
        {
            aLargeMember = std::make_unique<BigStruct>();
        }

        return *aLargeMember;
    }

public:
    // some method, which might depend on aLargeMember
    void someMethod();
};

如果您需要将其传递给类范围之外的对象,则存在悬挂引用的危险,因为拥有unique_ptr的已分配实例可能已被销毁。如果是这种情况,并且您确实想对此做出保证,那么unique_ptr就不合适了,因为您只能移动。相反,可以考虑使用shared_ptr并从GetLargeMember()返回实际的智能指针。

  

或者在当前情况下是否有针对堆的良好论证?

此处不反对使用堆,但是至少可以使用几种模式进行处理。例如,假设您打算创建大量实例,但同时创建的实例少得多,那么我将认真考虑pooling class Element的实例。