我们说我有两个功能。一个返回std::vector<int>
,另一个返回一个具有std::vector<int>
成员的类。
// using only the vector
std::vector<int> buildVector(){
std::vector<int> value;
// build the vector...
return value;
};
// using and returning a class instead
struct Something{
std::vector<int> m_vector;
}
Something buildSomething(){
Something value;
// build the object...
return value;
};
两个函数buildVector()
和buildSomething()
是否会执行它们返回的对象的浅层副本(避免不必要的堆分配)?或者他们不会吗?或者他们会有不同的行为?
我假设向量的内部指针仍然有效,除非我错了并且局部变量决定释放这个内存。 (他们会吗?)
我本身没有表现,但我不想进行不需要的分配。以这种形式编写函数对我所做的事情来说是最自然的。
我的编译器没有使用C ++ 11;与现有标准相比,现行标准会出现不同的情况吗?
答案 0 :(得分:4)
简短回答:在C ++ 11之前,它会对每个元素进行复制。它会在旁边分配缓冲区并进行memcpy等。非常快,非常高效,因为矢量存储连续的内存。通过值返回的堆栈向量的指针可能不再有效。
在C ++ 11中,Std lib人可以使用移动操作从一个向量中有效地“窃取”另一个向量,从而导致只有几个指针交换。这是特定于实现的,因此虽然您的编译器可能是C ++ 11,但Stdlib可能不是最新的。 C ++ 14使得它更加透明和令人敬畏,但同样,您的平台实现Stdlib可能无法更新以利用Move语义。
作为旁注,“三个规则”已成为五国统治。见Rule-of-Three becomes Rule-of-Five with C++11?
我希望找出语言伙伴是否可以做些什么...一些编译器魔术使其再次成为三个规则以防止代码膨胀。
答案 1 :(得分:1)
首先:在所有情况下,buildVector
和buildSomething
在复制/移动或任何数据方面都是完全等效的(除非您的编译器具有脑智能优化器)。
在C ++ 11之前,你受到了编译器的支配。代码具有“为value
分配所需内存,为return
值分配新内存并将数据复制到该内存中,以及(如果发生)将返回值复制到变量中的语义调用代码(再次,分配新内存并复制每个元素)“。这看起来很糟糕,但是这两个副本中的一个或两个可能是elided,所以(大多数或者全部)生产质量编译器,代码更有可能作为“分配{{1}所需的内存...并且突然使调用代码中的变量与value
完全相同“(没有(C ++可见)副本,移动或任何需要的任何东西)。
使用C ++ 11,可能发生“复制省略”的任何地方,必须将移动构造函数称为后备(如果没有可用的移动构造函数,则进一步回退到复制构造函数,以实现兼容性)。 value
的移动构造函数(在符合C ++ 11的实现中)通过从其操作数“窃取”内存来工作,创建一个新的std::vector
,其值与移动和离开的内容相同从std::vector
移动到有效但未指定的状态。 vector
操作不涉及内存分配或数据复制,只涉及固定数量的指针副本。