次のコードを考慮してください。
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);
}
なぜこれが機能するのですか?
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);
}
例は次のように縮小できます。
_vector<string> vec;
string str; // populate with a really long string
vec.Push_back(std::move(str));
_
これでも、「ローカルスタック変数を移動することは可能ですか」という疑問が生じます。理解しやすいように、余分なコードを削除するだけです。
答えはイエスです。上記のようなコードは_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);
_
対
_char* str; // populate with a really long string
char* other = str;
_
どちらの場合も、変数はスタック上にあります。しかし、データはそうではありません。
「小さな文字列の最適化」が有効な_std::string
_や、整数を含む構造体など、本当にすべてのデータがスタック上にある場合は、std::move()
で購入できます何もない。