【发布时间】:2017-03-21 05:47:43
【问题描述】:
如果向量总是提供连续的内存存储,编译器如何为空的 std::strings 分配内存?
我有一个向量,我已将多个类推送到其中,并将 std:string 作为私有成员。然后,我将对向量的引用作为参数传递给另一个方法。
堆中其他地方的字符串数据是否引用自向量的连续数组?
【问题讨论】:
如果向量总是提供连续的内存存储,编译器如何为空的 std::strings 分配内存?
我有一个向量,我已将多个类推送到其中,并将 std:string 作为私有成员。然后,我将对向量的引用作为参数传递给另一个方法。
堆中其他地方的字符串数据是否引用自向量的连续数组?
【问题讨论】:
为std::string 分配内存微不足道。
在内部,它会有某种指针指向一块内存,实际的字符串数据将存储在该内存块中。因此,为 std::string 分配内存只是为指针、size_t 或其他东西分配空间,也许还有更多原语。
例如,如果您有一个std::vector<std::string>,向量很容易为std::string 分配空间,因为它们只是k 字节,每个常量k。本次分配不涉及字符串数据。
【讨论】:
在这种情况下,内存中实际发生的事情的详细信息完全取决于您正在使用的特定 STL 实现。
话虽如此,我的印象是,在大多数实现中,向量和字符串都是用类似(非常简化的):
template<typename T>
class vector
{
//...
private:
T* _data;
};
class string
{
private:
char _smallStringsBuffer[kSmallSize];
char* _bigStringsBuffer;
};
向量的数据是根据容量在堆上动态分配的(默认初始化时有一个默认值,并在向向量添加元素时增长)。
字符串的数据是静态分配给小字符串的(“small”的实现依赖值),然后当字符串变大时动态分配。出现这种情况的原因有很多,但主要是为了更有效地处理小字符串。
你描述的例子是这样的:
void MyFunction(const vector<string>& myVector)
{
// ...
}
int main()
{
vector<string> v = ...;
// ...
MyFunction(v);
// ...
return 0;
}
在这种特殊情况下,只有向量 v 的基本数据将在堆栈中,因为 v._data 将在堆上分配。如果 v 的容量为 N,则 v._data 在堆中的大小将为 sizeof(string) * N,其中字符串的大小是一个常数,取决于 kSmallSize * sizeof(char) + sizeof (char*),基于上面字符串的定义。
对于连续数据,只有当向量中收集的所有字符串的字符数都少于 kSmallSize 时,它们的数据才会在内存中“几乎”连续。
这是性能关键代码的一个重要考虑因素,但老实说,我认为大多数人不会在这种情况下依赖标准 STL 的向量和字符串,因为实现细节会随着时间和不同平台而变化和编译器。此外,当您的代码超出“快速”路径时,您不会注意到,除非出现难以控制的延迟高峰。
【讨论】: