考虑一个像这样的类
class Element{
public:
// approx. size of ~ 2000 Byte
BigStruct aLargeMember;
// some method, which does not depend on aLargeMember
void someMethod();
}
Run Code Online (Sandbox Code Playgroud)
现在假设 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();
}
Run Code Online (Sandbox Code Playgroud)
基本上,这对应于/sf/answers/2565259441/中给出的建议 4 :
仅当有明确需要时才使用 new,例如:
特别大的分配会占用大部分堆栈(您的操作系统/进程将“协商”一个限制,通常在 1-8+ MB 范围内)
- 如果这是您使用动态分配的唯一原因,并且您确实希望对象的生命周期与函数中的作用域相关联,则应该使用本地 std::unique_ptr<> 来管理动态内存,并确保它无论您如何离开作用域,都会被释放:通过 return、 throw、break 等。(您还可以在类/结构中使用 std::unique_ptr<> 数据成员来管理对象拥有的任何内存。)
所以,我的问题是:在当前情况下,堆方法是否被认为是不好的做法?或者在本例中是否有任何反对堆的好的论据?先感谢您!
C++ 类成员:堆栈与堆分配
不要将基于堆栈的分配与堆分配对象的成员变量混淆。如果您有此类的堆分配对象:
class Element{
public:
// approx. size of ~ 2000 Byte
BigStruct aLargeMember;
// some method, which does not depend on aLargeMember
void someMethod();
}
Run Code Online (Sandbox Code Playgroud)
那么这里没有在堆栈上分配任何内容。
由于 aLargeMember 并不经常需要,并且考虑到 Element 的实例数量较多,动态创建 aLargeMember 是否有利?
您所描述的场景确实是动态内存分配的合法候选者,因为不这样做只会让您分配比您需要的更多的东西,而没有任何好处 - 所以您没有太多其他选择。
当前情况下的堆方法是否被认为是不好的做法?
这个问题有点太笼统了,很容易被解释为基于意见,但在给定片段的上下文中,您似乎指的是根据需要使用的基础分配成员变量的场景,因此,为了减轻必须手动维护代码中每个点的初始化所需的开销,最好进行惰性初始化。为此,您可以包装对此成员的访问,以确保返回已初始化的内容。只是为了说明,非常不是线程安全的,其想法是这样的:
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();
};
Run Code Online (Sandbox Code Playgroud)
如果您需要将其传递以在类范围之外使用,那么您将面临悬空引用的危险,因为拥有该对象的分配实例unique_ptr
可能已经被销毁。如果是这种情况,并且您确实想保证不会出现这种情况,那么unique_ptr
就不合适,因为您只能移动它。相反,请考虑使用 ashared_ptr
并从 中返回实际的智能指针GetLargeMember()
。
或者在本例中是否有任何反对堆的好的论据?
不反对在这里使用堆,但至少有几种模式可供您使用。例如,假设您打算创建如此大量的实例,但同时存在的实例要少得多,我会认真考虑池化class Element
.