8

以下は、書籍「C++ Gotchas」の項目 56 からの抜粋です。

Y オブジェクトの単純な初期化が、3 つの異なる方法のいずれかであるかのように記述されることは珍しくありません。

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

実際のところ、これら 3 つの初期化はすべて同じオブジェクト コードが生成される可能性がありますが、同等ではありません。a の初期化は直接初期化として知られており、期待通りの動作をします。初期化は、Y::Y(int) の直接呼び出しによって実行されます。

b と c の初期化はより複雑です。実際、それらは複雑すぎます。これらは両方ともコピーの初期化です。b の初期化の場合、値 1066 で初期化された、タイプ Y の匿名一時の作成を要求しています。次に、この匿名一時を、クラス Y のコピー コンストラクターへのパラメーターとして使用して、b を初期化します。最後に、匿名のテンポラリのデストラクタを呼び出します。

これをテストするために、データ メンバ (最後に添付されたプログラム) を使用して単純なクラスを作成したところ、驚くべき結果が得られました。c の場合、本で示唆されているのではなく、コピー コンストラクターによってオブジェクトが構築されたようです。

言語標準が変更されたのか、それとも単にコンパイラの最適化機能なのか、誰か知っていますか? Visual Studio 2008 を使用していました。

コードサンプル:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

出力:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

私が最も驚いたのは、d と e を構築した結果です。正確には、空のオブジェクトが作成され、次にオブジェクトが作成されて空のオブジェクトに割り当てられることを期待していました。実際には、オブジェクトはコピー コンストラクターによって作成されました。

4

3 に答える 3

16

構文

X a = b;

ここで、a と b は型であり、X は常にコピー構築を意味します。次のような任意のバリアント:

X a = X();

割り当てが行われておらず、使用されたこともありません。構築と割り当ては次のようになります。

X a;
a = X();
于 2010-03-17T14:05:28.093 に答える
6

コンパイラはケースを最適化し、 と同じにすることが許可されbcaます。さらに、コピー構築と代入演算子の呼び出しは、とにかくコンパイラによって完全に排除される可能性があるため、表示されるものは、異なるコンパイラまたはコンパイラ設定でさえ必ずしも同じではありません。

于 2010-03-17T14:03:33.723 に答える