2016-05-20 11 views
4

ん:STD文字列がクラッシュしなければならないが、私はクラスを持ってい

class A { 
    public: 
    string B; 
}; 

し、コード:両方のインスタンスの

A a1; 
a1.B = "abc"; 

printf("%p.\n", a1.B.c_str()); 

A a2(a1); 

printf("%p.\n", a2.B.c_str()); 

c_strさんは、同じ場所を参照してください(この私が理解し、コンストラクタをコピーしますコピーされたビット単位で、stringは内部でchar *にデータを格納し、ポインタがコピーされました。

しかし、このコードがクラッシュしないのはなぜですか?a1とa2はスタック変数です。 B'sもgeになりますこれらの文字列(同じメモリ位置を指す)の内部char *は2回削除されませんか?それはダブルクラッシュを引き起こす必要があります削除ですか? btw gccの最適化を無効にし、valgrindには何も表示されません。

+2

C++で「クラッシュすべき」と考えるのはほとんど常に間違っています。しかし、この場合、コードが持つと思う問題は文字列クラスによって処理されます。 – chris

答えて

13

いいえ、ポインタはではありません。がコピーされます。 std::stringのコピーコンストラクタは、の新しいバッファを作成し、他の文字列のバッファからデータをコピーします。

編集:C++標準を使用して、ポインタを共有するコピーオンセマンティクスを許可しましたが(これに伴い参照カウントが必要になります)、これはC++ 11以降は許可されませんでした。明らかにこれを行ったGCCのバージョンがありました。

+0

まず、Bのコピーコンストラクタはこの場合呼び出されますか?次に、なぜc_str()の同じアドレスが表示されるのですか? –

+1

@JaniBaramidze 1)はい2)gccの最適化はC++ 11では無効です(つまり* "Copy on write" *)。gcc5では削除されました。 –

+0

それは間違っています、バッファは変更が文字列に対して行われていない限り同じです。 – Caduchon

-2

ストリングコピーが実際にストリングを複製するので、両方のストリングが同じデータを持つ異なるメモリー位置を指し示すので、クラッシュしません。 GCC 4の

3

*

バッファを指しているインスタンスの数を知るために、文字列クラスの内部カウンタが、あります。カウンタを0にすると、インスタンスはメモリを解放する責任を持ちます。共有ポインタ(boostまたはC++ 11)と同じ動作です。

また、文字列が変更されると、バッファを共有する他のインスタンスの変更を避けるために新しいバッファが割り当てられます。

2

クラッシュしなければならないが、この文では、塩の粒で撮影する必要がありません

ありません。 C++には "クラッシュする必要がある"という概念はありません。これには、定義されていない動作の概念があり、クラッシュする可能性があります。それでも、コードには未定義の動作はありません。

両方のインスタンスの

c_str年代が同じ場所を参照してください(この私が理解し、 コンストラクタはビットごとにコピーされ、文字列は内部的に のchar *のデータを格納し、ポインタがコピーされてしまったコピー。

あなたは実装std::stringについて話している。あなたが代わりに安全であるとされていないどの操作を決定するために、そのインターフェースを見なければならない必要があります。

これ以外にも、コピーオンライトや「COW」と呼ばれる実装についてはis obsolete since C++11です。最新のGCCのバージョンはそれを放棄しました。

GCC 5 Changes, New Features, and Fixesを参照してください:

std::stringの新しい実装が代わりにコピー・オン・ライト参照カウントの 小さな文字列の最適化を使用して、デフォルトで有効になっています。

小文字の最適化は、たとえばVisual C++実装のstd::stringでも使用されているのと同じ手法です。これは完全に異なる方法で動作しますので、十分に新しいGCCバージョンを使用するか、Visual C++を使用すると正しいことがない場合、std::stringが内部でどのように動作するかについてのあなたの理解は、もはや正しくありません。

しかし、このコードがクラッシュしないのはなぜですか?

std::stringの操作はインターフェイスのドキュメントに従って正しく使用されているため、またコンパイラが完全に壊れていないためです。

基本的に、コンパイラが正しいコードのために動作するバイナリを作成する理由を尋ねています。

A1とA2がスタックされた変数、

はい(正しい用語は、オブジェクトが "自動記憶域期間" を持っていることであろう)。

文字列Bを分解すると、その文字列の内部char *(同じメモリ位置を指す)が2回削除されますか?

コンパイラのstd::string実装では、このようなことは起こりません。 COWをまったく使用しないか、デストラクタに共有バッファがすでに削除されているかどうかをチェックするコードが含まれています。

古いGCCバージョンを使用している場合は、std::string実装のソースコードを見れば、それがどのように実行されたかを知ることができます。結局のところ、オープンソースだが、恐ろしく見えるかもしれないので注意する。例えば、ここにan older GCC versionのデストラクタコードだ:

~basic_string() 
{ _M_rep()->_M_dispose(this->get_allocator()); } 

その後(同じファイルに)_M_disposeを見て、あなたはそれがさまざまなチェックとsynchronisationsと非常に複雑な実装だとわかります。

std::stringをコピーするの膨大な行為がクラッシュにつながる場合には、クラス全体が完全に無意味だろう、それはないだろう:

はまた、この検討してください?

関連する問題