3

「サンプル」への s と s2 の内部ポインタが等しくないことに驚きました。説明は何ですか?

#include <string>
#include <cassert>

int main()
{
    std::string s("sample"); 

    std::string s2(std::move(s)); 

    assert(
        reinterpret_cast<int*>(const_cast<char*>(s.data())) ==
        reinterpret_cast<int*>(const_cast<char*>(s2.data()))
        ); // assertion failure here    

    return 1;
}
4

3 に答える 3

3

なぜそれらが同じであるべきだと思いますか?その移動コンストラクターを使用して構築s2しています。sこれにより、データの所有権が に転送さsれ、「空の」状態のままになりますs2s標準は、これが何を伴うかを詳細に指定していませんが、sその後 (最初に再割り当てせずに) のデータにアクセスすることは未定義です。

の非常に単純化された (そして不完全な) バージョンはstring、次のようになります。

class string {
    char* buffer;

public:

    string(char const* from)
        : buffer(new char[std::strlen(from) + 1])
    {
        std::strcpy(buffer, from);
    }

    string(string&& other)
        : buffer(other.buffer)
    {
        other.buffer = nullptr; // (*)
    }

    ~string() {
        delete[] buffer;
    }

    char const* data() const { return buffer; }
};

data()メンバーが平等ではない理由を示してくれることを願っています。でマークされた行を省略した場合(*)、 の最後で内部バッファを 2 回削除mainsますs2。ポインタをリセットbufferすると、これが起こらないことが保証されます。

于 2013-04-07T11:03:54.977 に答える
3

それぞれstd::stringが指す独自のバッファーを持っています。一方を他方から移動したという事実は、1 つのバッファーを共有するわけではありません。を初期化するs2と、 からバッファを引き継ぎ、そのバッファsの所有者になります。s同じバッファを「所有」することを避けるために、sのバッファを新しい空のバッファに設定するだけです(そして、それsが現在の責任です)。

技術的には、いくつかの最適化も含まれます。ほとんどの場合、空の文字列または非常に小さな文字列に明示的に割り当てられた実際のバッファーはありませんが、代わりに の実装はのメモリ自体のstd::string一部を使用します。std::stringこれは通常、STL では小さな文字列の最適化として知られています。

また、sは移動されていることに注意してください。そのため、コードのデータへのアクセスは違法です。つまり、何かを返す可能性があります。

于 2013-04-07T11:04:22.937 に答える
2

string値を既知の値に置き換える前に、moved-from を使用しないでください。

ライブラリ コードは引数に有効な値を残す必要がありますが、型または関数が別の方法で文書化されていない限り、結果の引数値に他の制約はありません。これは、moved from 引数を再度使用しないことが一般的に最も賢明であることを意味します。再度使用する必要がある場合は、事前に既知の値で再初期化してください。

ライブラリは必要なものを文字列に挿入できますが、最終的に空の文字列になる可能性が非常に高くなります。これは、cppreference から例を実行すると生成されるものです。ただし、移動元のオブジェクト内で特定の何かが見つかると期待するべきではありません。

于 2013-04-07T11:05:04.187 に答える