2016-06-23 5 views
-2

次のコードでは、文字列はクラスFoo内にカプセル化されています。std :: stringを値で返すのは安全ですか?

Foo :: getText()を呼び出すと、文字列が値で返されます。これにより、文字列オブジェクトの2番目のインスタンスが作成されますが、両方の文字列オブジェクトは現在、ヒープ上の同じcharバッファを指しています。

Fooインスタンスが削除されると、カプセル化された文字列が自動的に破棄され、ヒープ上のcharバッファが削除されます。

getText()は文字列を値で返しますが、結果として得られる文字列オブジェクトは、元の文字列オブジェクトの存続期間に依然として依存し、相互にポイントするcharバッファを保持します。

これは、retvalの端末への印刷が、すでにヒープ上でフリーになっているメモリへの無効なアクセスであることを意味していませんか?

class Foo 
{ 
    Foo(const char* text) : 
     str(text) 
    { 
    } 

    std::string getText() 
    { 
     return str; 
    } 

    std::string str; 
}; 

int main() 
{ 
    Foo pFoo = new Foo("text"); 
    std::string retval = foo.getText(); 
    delete pFoo; 
    cout << retval; // invalid memory access to char buffer? 
} 

は、私は多くの人が列を値によって返されたので、彼らはFooの中の元の文字列の寿命を心配する必要はない、ということを前提と考えます。この問題は厳密には文字列には関係しませんが、実際には破棄時には自由にカプセル化されたポインタを持つクラスに適用されます。しかし、ストリングに関してはここでベストプラクティスは何ですか?

  1. 文字列を値で返すことはありませんか?
  2. 元の文字列の有効期間が保証されている場合は、文字列を値で返すだけですか?
  3. 常に文字列のコピーを作成しますか? return std::string(retval.c_str());
  4. getText()の発信者との契約を強制しますか?

EDIT:

は、私はRVOに惑わされたと思います。この例の3つの文字列はすべて、同じアドレスにc_strを返します。 RVOは責任がありますか?

class Obj 
{ 
public: 
    Obj() : s("text") 
    { 
     std::printf("%p\n", s.c_str()); 
    } 

    std::string getText() { return s; } 

    std::string s; 
}; 

int main() 
{ 
    Obj* pObj = new Obj(); 
    std::string s1(pObj->getText()); 
    std::string s2 = pObj->getText(); 
    delete pObj; 
    std::printf("%p\n", s1.c_str()); 
    std::printf("%p\n", s2.c_str()); 
} 

結果:

0x600022888 
0x600022888 
0x600022888 
+1

@πάνταῥεῖこれは、 'std :: vector'ではなく' std :: string'に関するものです。もう一つのデュエルミーマーミス。 –

+5

* getText()は値によって文字列を返しますが、結果の文字列オブジェクトは元の文字列オブジェクトの存続期間に依存しており、相互に指し示すcharバッファを保持します。あなたはどこからこれを取得しましたか、またはあなたは推測していますか? C++の仕組みを理解する上でJavaを参考にしているのであれば、C++はJavaではありません。 – PaulMcKenzie

+1

*両方の文字列オブジェクトがヒープ上の同じcharバッファを指すようになりました* - クラスが誤ったまたはコピーされていないコピーコンストラクタ、代入演算子、およびデストラクタを持つ自家製文字列クラスで、次に、同じバッファを指すことができます**誤って**。しかし、専門家と専門家によって書かれた "std :: string"については、正しいコピーセマンティクスを持っています。 – PaulMcKenzie

答えて

9

これは、文字列オブジェクトの第2のインスタンスを作成し、両方の文字列オブジェクトは、現在のヒープ上の同じ文字バッファを指します。

いいえ、そうではありません。

std::stringは、その内容を所有しています。 std::stringをコピーすると、そのバッファがコピーされます。

文字列が値によって返されるため、多くの人は、Foo内の元の文字列の存続期間を気にする必要はないと思います。

これらの人々は正しいです。 「共有」はありません。

あなたの価値によるリターンは上品であり、それについてもっと考える必要はありません。

4

私はLightnessRacesInOrbitの答えを@にいくつかのポイントを追加したい:

は値によって、文字列を返すことはありませんか?

参照番号またはポインタでローカルstringを返さないでください。実際には、何かを参照またはポインタでローカルに返すことはありません。値によって、ちょうど良いです。

元の文字列の寿命は を保証されている場合にのみ値で文字列を返しますか?

もう一度考えてみましょう。元の生存期間が保証されている場合は、(参照)ま​​たはのポインタのみを返してください。

常に文字列のコピーを作成しますか? std :: stringを返します(retval.c_str());

文字列を移動できない場合(RVO/NRVO)、自動的にこれが実行されます。手動でコピーする必要はありません。

getText()の呼び出し元との契約を強制しますか?あなたはとにかく

コピーを入手して、私は多くの人が列を値によって返されたので、彼らは内の元の文字列の寿命を心配する必要はありません、ということを前提と考える必要はない

Foo。この問題は厳密には文字列には関係しませんが、実際には破棄時には自由にカプセル化されたポインタを持つクラスに適用されます。

これらの仮定は正しいです。 (作業中の)コピーコンストラクタを持つクラスは、必要に応じて頻繁にコピーすることができ、コピーされた各インスタンスは他のクラスと完全に独立しています。コピーコンストラクタは、ヒープスペースのコピーを処理します。

+0

あなたの答えに力を入れてくれてありがとう。 – Jason

関連する問題