私は人々がC ++でオブジェクトを作成することをよく見ました
Thing myThing("asdf");
これの代わりに:
Thing myThing = Thing("asdf");
少なくともテンプレートが含まれていない限り、これは(gccを使用して)機能するようです。私の質問は、最初の行が正しいのですか、そうであればそれを使用する必要がありますか?
私は人々がC ++でオブジェクトを作成することをよく見ました
Thing myThing("asdf");
これの代わりに:
Thing myThing = Thing("asdf");
少なくともテンプレートが含まれていない限り、これは(gccを使用して)機能するようです。私の質問は、最初の行が正しいのですか、そうであればそれを使用する必要がありますか?
回答:
両方の行は実際には正しいですが、微妙に異なることを行います。
最初の行は、フォーマットのコンストラクターを呼び出すことにより、スタック上に新しいオブジェクトを作成しますThing(const char*)。
2つ目はもう少し複雑です。基本的には次のことを行います
Thingコンストラクターを使用してタイプのオブジェクトを作成するThing(const char*)Thingコンストラクターを使用してタイプのオブジェクトを作成するThing(const Thing&)~Thing()手順1で作成したオブジェクトを呼び出すThing myThing = Thing(...)代入演算子を使用せずThing myThing(Thing(...))、と同じようにコピー構築され、デフォルト構築は含まれませんThing(編集:投稿はその後修正されました)
私はあなたが実際に意味する2行目で仮定します:
Thing *thing = new Thing("uiae");
これは、新しい動的オブジェクト(動的バインディングとポリモーフィズムに必要)を作成し、それらのアドレスをポインターに格納する標準的な方法です。あなたのコードは、(1が渡され、すなわち2つのオブジェクトを作成し、JaredParは説明何をconst char*、他の合格const Thing&)した後、(デストラクタを呼び出して~Thing()最初のオブジェクト(上)const char*1)。
対照的に、これ:
Thing thing("uiae");
現在のスコープを終了すると自動的に破棄される静的オブジェクトを作成します。
コンパイラーは2番目の形式を最初の形式に最適化することもできますが、そうする必要はありません。
#include <iostream>
class A
{
public:
A() { std::cerr << "Empty constructor" << std::endl; }
A(const A&) { std::cerr << "Copy constructor" << std::endl; }
A(const char* str) { std::cerr << "char constructor: " << str << std::endl; }
~A() { std::cerr << "destructor" << std::endl; }
};
void direct()
{
std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
A a(__FUNCTION__);
static_cast<void>(a); // avoid warnings about unused variables
}
void assignment()
{
std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
A a = A(__FUNCTION__);
static_cast<void>(a); // avoid warnings about unused variables
}
void prove_copy_constructor_is_called()
{
std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
A a(__FUNCTION__);
A b = a;
static_cast<void>(b); // avoid warnings about unused variables
}
int main()
{
direct();
assignment();
prove_copy_constructor_is_called();
return 0;
}
gcc 4.4からの出力:
TEST: direct
char constructor: direct
destructor
TEST: assignment
char constructor: assignment
destructor
TEST: prove_copy_constructor_is_called
char constructor: prove_copy_constructor_is_called
Copy constructor
destructor
destructor
簡単に言うと、両方の行が「new」のようにヒープ上ではなくスタック上にオブジェクトを作成します。2行目は実際にはコピーコンストラクターへの2番目の呼び出しを含むため、回避する必要があります(コメントに示されているように修正する必要もあります)。小さいオブジェクトにはできるだけ高速でスタックを使用する必要がありますが、オブジェクトがスタックフレームよりも長く存続する場合は、明らかに間違った選択です。
理想的には、コンパイラーが2番目を最適化しますが、必須ではありません。最初が最善の方法です。ただし、C ++でのスタックとヒープの違いを理解することは非常に重要です。自分のヒープメモリを管理する必要があるためです。
私は少し遊んでみましたが、コンストラクターが引数を取らない場合、構文がかなりおかしくなったようです。例を挙げましょう。
#include <iostream>
using namespace std;
class Thing
{
public:
Thing();
};
Thing::Thing()
{
cout << "Hi" << endl;
}
int main()
{
//Thing myThing(); // Does not work
Thing myThing; // Works
}
つまり、Thing myThingをブラケットなしで記述するだけで実際にコンストラクターが呼び出され、Thing myThing()はコンパイラーに関数ポインターなどを作成させます?? !!
JaredParの回答に追加
1-通常のctor、2番目の関数のようなctor、一時オブジェクト。
このソースをここhttp://melpon.org/wandbox/のどこかに異なるコンパイラでコンパイルします
// turn off rvo for clang, gcc with '-fno-elide-constructors'
#include <stdio.h>
class Thing {
public:
Thing(const char*){puts(__FUNCTION__ );}
Thing(const Thing&){puts(__FUNCTION__ );}
~Thing(){puts(__FUNCTION__);}
};
int main(int /*argc*/, const char** /*argv*/) {
Thing myThing = Thing("asdf");
}
そして、結果が表示されます。
ISO / IEC 14882 2003-10-15から
8.5、パート12
1番目、2番目の構造は直接初期化と呼ばれます
12.1、パート13
関数表記の型変換(5.2.3)を使用して、その型の新しいオブジェクトを作成できます。[注:構文は、コンストラクターの明示的な呼び出しのように見えます。] ...この方法で作成されたオブジェクトには名前がありません。[注:12.2は一時オブジェクトの存続時間を説明しています。] [注:明示的なコンストラクター呼び出しは左辺値を生成しません。3.10を参照してください。]
RVOについて読む場所:
12特別なメンバー関数/ 12.8クラスオブジェクトのコピー/パート15
特定の基準が満たされている場合、オブジェクトのコピーコンストラクターやデストラクタに副作用がある場合でも、実装はクラスオブジェクトのコピー構築を省略できます。
そのようなコピー動作を表示するには、コメントからコンパイラフラグを使用してオフにしてください)