All*_*uer 20 c++ move move-semantics c++11
请考虑以下代码:
struct MyStruct
{
int iInteger;
string strString;
};
void MyFunc(vector<MyStruct>& vecStructs)
{
MyStruct NewStruct = { 8, "Hello" };
vecStructs.push_back(std::move(NewStruct));
}
int main()
{
vector<MyStruct> vecStructs;
MyFunc(vecStructs);
}
Run Code Online (Sandbox Code Playgroud)
为什么这样做?
在调用MyFunc时,返回地址应放在当前线程的堆栈上.现在创建NewStruct对象,它也应该被放置在堆栈上.使用std :: move,我告诉编译器,我不打算再使用NewStruct引用了.他可以偷走记忆.(push_back函数是具有移动语义的函数.)
但是当函数返回并且NewStruct超出范围时.即使编译器不会从堆栈中删除最初存在的结构所占用的内存,他至少也要删除先前存储的返回地址.
这将导致碎片堆栈,未来的分配将覆盖"移动"的内存.
有人可以向我解释一下吗?
编辑:首先:非常感谢您的回答.但是从我学到的东西,我仍然无法理解,为什么以下不能像我期望的那样工作:
struct MyStruct
{
int iInteger;
string strString;
string strString2;
};
void MyFunc(vector<MyStruct>& vecStructs)
{
MyStruct oNewStruct = { 8, "Hello", "Definetly more than 16 characters" };
vecStructs.push_back(std::move(oNewStruct));
// At this point, oNewStruct.String2 should be "", because its memory was stolen.
// But only when I explicitly create a move-constructor in the form which was
// stated by Yakk, it is really that case.
}
void main()
{
vector<MyStruct> vecStructs;
MyFunc(vecStructs);
}
Run Code Online (Sandbox Code Playgroud)
Yak*_*ont 26
首先,std::move不动,std::forward不转发.
std::move是对右值参考的强制转换.按照惯例,rvalue引用被视为"允许您将数据移出的引用,因为调用者承诺它们实际上不再需要该数据".
在围栏的另一侧,rvalue引用隐式绑定到std::move临时对象的返回值(在某些情况下,从函数返回本地时),以及使用临时或移动的成员时来自对象.
在采用右值参考的函数中发生的事情并不神奇.它不能直接在相关对象内声明存储.然而,它可以撕掉它的内脏; 它有权(按照惯例)弄乱其参数内部状态,如果它能以这种方式更快地进行操作.
现在,C++将自动为您编写一些移动构造函数.
struct MyStruct
{
int iInteger;
string strString;
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,它会写出大致如下所示的内容:
MyStruct::MyStruct( MyStruct&& other ) noexcept(true) :
iInteger( std::move(other.iInteger) ),
strString( std::move(other.strString) )
{}
Run Code Online (Sandbox Code Playgroud)
即,它将执行元素移动构造.
移动整数时,没有任何有趣的事情发生.弄乱源整数的状态没有任何好处.
移动时std::string,我们可以获得一些效率.C++标准描述了当你从一个移动std::string到另一个时会发生什么.基本上,如果源std::string使用堆,则堆存储将传输到目标std::string.
这是C++容器的一般模式; 当你从它们移动时,它们窃取源容器的"堆分配"存储并在目标中重用它.
请注意,源std::string仍然是一个std::string,只有一个"胆量被撕掉".大多数像容器一样的容器都是空的,我不记得是否std::string保证(它可能不是由于SBO),现在它并不重要.
简而言之,当你从某个东西移开时,它的内存不会被"重用",但它拥有的内存可以被重用.
在你的情况下,MyStruct有一个std::string可以使用堆分配的内存.这个堆分配的内存可以移动到MyStruct存储的中std::vector.
进一步沿着兔子洞走,"Hello"可能是如此短暂以至于SBO发生(小缓冲区优化),并且std::string根本不使用堆.对于这种特殊情况,由于moveing ,可能几乎没有性能改进.
Joh*_*nck 10
您的示例可以简化为:
vector<string> vec;
string str; // populate with a really long string
vec.push_back(std::move(str));
Run Code Online (Sandbox Code Playgroud)
这仍然提出了一个问题,"是否可以移动本地堆栈变量." 它只是删除了一些无关的代码,使其更容易理解.
答案是肯定的. 像上面这样的代码可以受益,std::move因为 - std::string至少如果内容足够大 - 将实际数据存储在堆上,即使变量在堆栈上也是如此.
如果你不使用std::move(),你可以期望像上面这样的代码复制内容str,这可能是任意大的.如果使用std::move(),则只复制字符串的直接成员(移动不需要将旧位置"清零"),并且将使用这些数据而无需修改或复制.
这基本上是这个之间的区别:
char* str; // populate with a really long string
char* other = new char[strlen(str)+1];
strcpy(other, str);
Run Code Online (Sandbox Code Playgroud)
VS
char* str; // populate with a really long string
char* other = str;
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,变量都在堆栈中.但数据不是.
如果你有一个真正所有数据都在堆栈中的情况,比如有效std::string的"小字符串优化",或者包含整数的结构,那么std::move()你什么都不买.