ProgrammingC++ 開発者

値渡しで関数にクラスのオブジェクトを渡すと何が起こり、コピーコンストラクタを実装する際の注意点は何ですか?

Hintsage AIアシスタントで面接を突破

回答

C++で値渡しでオブジェクトを関数に渡すと、コピーコンストラクタを使用してオブジェクトのコピーが作成されます。クラスにユーザー定義のコピーコンストラクタが定義されている場合、それが関数の引数としての一時オブジェクトを初期化するために呼び出されます。定義されていない場合、コンパイラによってデフォルトが使用され、ビット単位のコピー(shallow copy)が実行されます。

注意点:

  • クラスがリソースを管理している場合(例えば、生のポインタを持っている)、デフォルトで生成されたコピーコンストラクタはメモリの二重解放やリークにつながるエラーを引き起こします。
  • コピーコンストラクタがリソースを正しくコピーしていることを確認する必要があります(通常はdeep copy)。

例:

class StringWrapper { char* data; public: StringWrapper(const char* str) { data = new char[strlen(str) + 1]; strcpy(data, str); } // エラー: shallow copy StringWrapper(const StringWrapper& other) : data(other.data) {} ~StringWrapper() { delete [] data; } }; void foo(StringWrapper s) { // ... } int main() { StringWrapper s1("hello"); foo(s1); // UB!!! return 0; }

トリック質問

"ポインタを持つクラスでコピーコンストラクタを次のように定義した場合、何が起こりますか:MyClass(const MyClass &other) : data(other.data) {}? これによってどのような結果になりますか?"

正しい回答: このようなコピーコンストラクタは、コピー元と同じメモリ領域を指すオブジェクトを作成します。二つのオブジェクトが破棄されると、メモリが二重に解放され(double free)、未定義の動作を引き起こします。deep copyを実装する必要があります:

MyClass(const MyClass &other) { data = new int(*other.data); }

このテーマの詳細を知らないことによる実際のエラーの例


物語

大規模なサーバープロジェクトで、内部に"生"の配列を持つオブジェクトのコンテナが、標準のコピーコンストラクタ(shallow copy)を使用して利用されることがありました。オブジェクトを値渡しで渡すと、ダブルフリーが発生し、アプリケーションがクラッシュし、プロダクション環境でのみキャッチされていました。


物語

古いC++画像処理ライブラリでは、コピーコンストラクタがグラフィックバッファをコピーせず、片方のコピーの変更がもう片方に影響を与え、インターフェースで予期しないバグを引き起こしました。


物語

パスワードの内部ストレージシステムの一つからの関数からの値渡しの結果、オブジェクトが破棄されるときに一時オブジェクトがクリーンアップされ(shallow copy)、実際のオブジェクトがゼロポインタを保持し、リークはセキュリティ監査の過程で偶然発見されました。