我很习惯 C#,其中struct
s 最好只包含unmanaged
类型的成员,以便sizeof()
是struct
预先确定的并且可以完全视为值类型。
当一个人写下类似这样的内容时:
std::vector<T> myVec;
作为 C++ 头文件中 a 的成员struct
,我的理解是,这会std::vector<T>
class
在堆栈上分配 a 的实例,而不是class
es 通常在堆上通过指针引用的位置(尽管我在 C++ 中知道这一点struct
和class
定义)比 C# 中的相似得多)。
我想知道的是,使用像std::vector<T>
,这样的引用类型的一般内存含义是什么std::string
,std::map<T,T>
就好像它们是像这样属于堆栈的值类型一样?最重要的是:
如果我使用这些示例类型作为 a 的成员,我的
struct
大小仍然是固定的吗struct
?我是否最好使用指向这些类型的指针作为我的成员
struct
,以便我知道该成员在(例如)64 位机器上有 8 个字节大?struct
像这样的集合的堆栈分配实例(structInstA = structInstB
) 是否具有作为我的各个成员的浅拷贝的成员std::
,或者它们是否是具有相同元素的唯一成员?
谢谢阅读!
在 C++ 中,结构本身始终具有固定大小;某些编译器支持可变长度结构作为扩展,但它不是语言标准的一部分,并且这些类型都不需要它。
std::vector
、std::map
和std::string
Company 执行动态内存分配,因此结构本身仅存储少量指针(例如vector
,分配容量指针的开始、结束和结束),并且根据需要从堆中分配任何可变长度数据。不,使用指针只会进一步碎片化你的内存。考虑指针的主要原因是,如果您想在结构体的多个实例之间共享同一个
std::shared_ptr
容器(例如,与),或者在 的特定情况下std::string
,允许转移所有权,而不会潜在地使std::string_view
底层数据无效(某些实现中使用的小字符串优化意味着字符串数据的迭代器和视图可能会因移动而失效)。如果将它们存储为原始值而不是指针,则副本(堆栈分配或其他方式)将(通常)是各种包含的集合的深层副本。如果您使用
std::shared_ptr
-wrapped 版本的集合,它将执行非常浅的副本(副本将引用与原始集合完全相同的集合,但其中任何一个都可以重新分配给集合的新版本以中断连接) 。补充笔记:
C++ 结构体和类是相同的,除了一个区别:结构体成员默认具有公共可见性,类默认具有私有可见性。两者都可以位于堆栈上(如果在函数体内声明为 non-
static
、 non- ,没有或其他分配助手之类的),两者都可以位于堆上(使用/ / 等),或者它们可以位于全局内存中(如果在函数外部声明,或在函数内部声明为s)。constexpr
new
std::make_shared
new
std::make_shared
static
但在您提到的所有容器类型中,存储在堆栈/堆/全局内存中的位都很微不足道,只是少数指针。它们包含的所有数据都位于堆上,由类本身自动管理。当复制到该类型的新实例时,会分配新的堆内存,复制所有包含的数据(这可能会调用额外的堆分配),并且此后两个实例完全不相关(某些复制构造函数可能不会作为深层复制运行,与
shared_ptr
s 一样,但它们是例外,而不是规则)。这就是std::move
存在的原因;如果您不需要数据的两个副本,您只想转移数据的所有权(例如,您已经填充了 astd::vector
并希望将其移交给其他struct
将拥有它的人),std::move
降低了从分配和深度复制所有内容(可能非常昂贵)到仅复制指针并将源指针清零(通常需要六个处理器周期)的成本。