の違いは何ですか
int i=0;
と
int i(0);
int *p=new int;
と
int *p=new int(0);
int *p=new int
まだコピー初期スタイルですか?
いつ使用しint i=0;
ないのnew int(0)
ですか?
の違いは何ですか
int i=0;
と
int i(0);
int *p=new int;
と
int *p=new int(0);
int *p=new int
まだコピー初期スタイルですか?
いつ使用しint i=0;
ないのnew int(0)
ですか?
最初に、少し異なる質問に答えます。
次のようなclass
or struct
(ほとんど同じです) があるとします。
struct Foo {
int value; // Foo stores an int, called value
Foo(int v):value(v) {}; // Foo can be constructed (created) from an int
explicit Foo(double d):value(d) {}; // Foo can be constructed (created) from a double
// ^^^ note that keyword. It says that it can only be EXPLICITLY created from a double
};
これは によく似てint
いますが、いくつかの違いがあります。
Foo i = 0;
上記はint
リテラルを作成し、コンストラクターFoo i
を使用してそれをFoo(int v)
構築します。
Foo i(0);
上記はint
リテラルを作成し、コンストラクターFoo i
を使用してそれをFoo(int v)
構築します。私は自分自身を繰り返したことに注意してください。
Foo i = Foo(0);
上記はint
リテラルを作成し、コンストラクターFoo i
を使用してそれをFoo(int v)
構築し、それからコピー構築Foo i
します。ただし、標準では、コンパイラがコピー コンストラクターを "省略" (スキップ) し、代わりにリテラルFoo i
から直接構築することを許可しています。int
0
Foo* i = new Foo;
これはフリー ストア (通常は実装され、ヒープと呼ばれます) に移動し、 を格納するのに十分なメモリを取得してからFoo
、デフォルトでそれを構築します。次に、このオブジェクトのアドレスを返します。このアドレスはFoo
、ポインタの初期化に使用されますFoo* i
。ここで、デフォルトのコンストラクターが uninitialized のままであることに注意しFoo
てvalue
ください。これはFoo
上記の私の実装の欠陥です (私の意見では)。特別な場合を除いて、これを行うことはめったにありません。
厄介なことに、整数 ( char
、int
、long
、unsigned char
など)、浮動小数点 (double
またはfloat
) リテラル、およびポインター リテラルはすべてこのプロパティを共有します。デフォルトでは、これらはどの値にも初期化されません。
したがって、それらが明示的に初期化されていることを確認する必要があります。の場合、Foo
次の行を追加します。
Foo():value() {}
するだけで十分です。
Foo* i = new Foo(0);
これはフリー ストア (通常は実装され、ヒープと呼ばれます) に移動し、 a を格納するのに十分なメモリを取得Foo
し、整数リテラル 0 でそれを構築します。次に、このFoo
オブジェクトのアドレスを返します。これは、ポインタの初期化に使用されます。Foo* i
.
現在、フリー ストア内のメモリは通常、一度要求すると、それを返すまで、またはプログラムがシャットダウンするまで、使用のために予約されたままになります。それを返すにはdelete
、同じポインターを呼び出します。どちらの場合も、オブジェクト (Foo
この場合) のデストラクタが呼び出されます (Foo
デストラクタがないため、これはスキップされます)。その後、メモリはフリー ストアに戻されます。への後の呼び出しで使用されnew
ます。
これを追跡するのは大変な作業であり、大量のエラーの原因となるため、 の呼び出しは避ける必要がありますnew
。メモリのブロックを管理するために を使用する、RAII として知られる手法を介して独自のライフタイムを管理するヒープ上に および を使用してオブジェクトを作成する、ポインタのライフタイムを厳密に制御したい場合にnew
を使用するなど、呼び出しを回避する方法は多数あります。 (残念ながら存在しません)。std::vector
shared_ptr
make_shared
unique_ptr
make_unique
では、さらに先に進みましょう。
Foo i = 0.0;
これはコンパイルに失敗します。のコンストラクターは明示的であると言いましたがFoo(double)
、上記は非明示的なコンストラクターの呼び出しのみを選択しています。一方で:
Foo i(0.0);
Foo i = Foo(0.0);
これらはどちらも明示的なコンストラクターを呼び出して問題なく動作します。
次に、C++11 は均一な初期化をもたらします。何かを初期化したいものを に入れる代わりに、()
それを{}
波括弧に入れます。
Foo i{0.0};
Foo i = {0};
など には、ベースの構文{}
と比較していくつかの違いがあります。最も重要なのは、ほとんどの厄介な解析の問題を回避できることです。その他の違いには、初期化子リストの動作が含まれます。これは、何かを明示的に自明に構築することを扱います (は名前付きを構築しませんが、構築します)。()
int x()
int
x
int x{}
そういえば、実際の質問に戻りましょう。
int
いくつかの点で私のものとは異なりstruct Foo
ます。
まず、それはclass
またはではありませんstruct
。したがって、その動作は、記述したコードによって決定されるのではなく、代わりに標準によって広範に記述されています。たまたま、C++ は のようなプリミティブ型を のようint
な単純なユーザー定義型のように動作させようとしますがFoo
、これは便利です。
そのため、「コピー コンストラクター」は呼び出されint
ず、コンストラクターもデストラクタもまったくありませんint
が、そのようなコンストラクターがある場合は、ほぼ正確に「あたかも」動作します。
int i = 0;
整数リテラル 0 を作成し、それで初期化int i
します。コンパイラはこれを省略int i
し、値 0 の整数を直接作成するだけです。の場合int
、違いを直接観察する方法はありません。
int i(0);
非明示的なコンストラクタがないint i = 0
ため、と同じです。int
これは構文が異なるだけです。
ここで、最も厄介な解析の問題があります。入力した場合
int i = int();
と同じ結果が得られますint i = 0
が、入力した場合
int i();
何が起こるかというと、コンパイラは「それは、ゼロ引数を取って戻り値を持つ i という名前の関数である可能性がある」と言いint
、さまざまな迷惑な理由から、デフォルトで初期化されたint
. したがってi
、整数の代わりに名前が付けられた前方宣言された関数を取得します。
前述のように、これを回避する方法は、常に次の構文を使用することです。
int i{};
C++11 コンパイラで。
次に、
int* p = new int;
これは、フリー ストアに構築された (初期化されていない) デフォルトを作成し、int
それへのポインターを変数に割り当てますint *p
。コピーが発生しましたが、これはポインタのコピーであり、int
.
int *p=new int(0);
ほとんど同じですが、int
作成されたフリー ストアの値は、初期化されていない代わりに 0 になります。
どちらの場合も、delete
によって返される値に対して 1 回だけ呼び出す必要がありますnew
。そうしないと、メモリ リークと呼ばれます。ポインタ値を使用した後に使用すると、未定義の動作が発生し、通常はメモリが破損します。ほとんどの場合、実行したことが危険であるという警告はプログラムから出されませんが、プログラムは意味をなさない完全にランダムな処理を実行できるようになり、有効な C++ プログラムであることに変わりはありません。それぞれnew
を正確に 1 つdelete
に揃え、 の後に誰もポインターを使用しないようにし、できるだけ早くdelete
呼び出すことは、プログラミング言語のカテゴリ全体が開発されたほど面倒なプログラムであり、その主な売りの特徴は、開発者が対処する必要がなくなることです。delete
それと。
したがって、呼び出しは避けてくださいnew
。
ああ、これでは十分に長くないので、上記の「最も厄介な構文解析」の使い方が正しくないことに注意してください。「最も厄介な解析」は実際には次のとおりです。
int x(int());
それよりも
int x();
1 つ目はコンストラクター呼び出しまたは関数のいずれかである可能性があるため、2 つ目は標準で許可されていないためコンストラクター呼び出しにすることはできません。
しかし、私と他のほとんどの人は、 を作る構文解析規則を見つけたint x();
ので、それを最後から 2 番目の厄介な構文解析と呼んでも、それほど間違っていないでしょう。それはあなたが単純にやるべきだと思っていること(デフォルトのconstructedを作成するx
)をまだしていないので、かなり厄介です。
int i=0;
int i(0);
これらは同じですが、2 つの異なる初期化構文があります (最初は C に似ており、2 番目はコンストラクター スタイルです)。原則として、クラスを扱う場合はわずかに異なります (最初のものはコピー コンストラクターへの呼び出しを意味しますが、2 番目のものはそうではありません) int
。
int *p=new int;
int *p=new int(0);
最初のものでint
は初期化されていません(そのメモリ位置にある値は何でもあります)。2 番目のケースでは、int
は 0 に初期化されます。
しかし、最も重要なことは、これらは最初のものに関して2つの完全に異なる獣です. タイプ の自動変数を宣言しているのではなく、int
動的に割り当てられた 2 つの を指す へのポインタを宣言しています。int
int
違いは深いです:int i=0
メモリは自動的に管理され (自動保存期間があります)、変数はスコープ外になると破棄されます。new
自動割り当て解除メソッドを持たないフリーストア (いわゆるヒープ) からメモリを割り当てると、明示的に解放する必要があります (最新delete
の C++ スマート ポインターでは通常、動的オブジェクトの有効期間を自動的に管理するために使用されます)。オブジェクト)。
new
通常、自動ストレージ期間が適切な選択ではない場合 (たとえば、それらの s を現在のスコープより長く存続させたい場合int
や、複数のオブジェクト間で共有したい場合など)、または大きすぎるものを割り当てている場合に正確に使用されます。ローカル変数にとどまります(通常の実装では、ローカル変数はサイズが制限されているスタックに置かれますが、2 つint
の s については有効な問題ではありません)。
「通常」の場合int
、現在のスコープで「死ぬ」必要があるローカル変数は、new
通常は良い考えではありません。
それでも、動的割り当ての詳細については、C++ の本を確認してください。
C および初期の C++ では、int i=0; しか使用できませんでした。
パターンは一般的な型のint i(0);
コンストラクタと同じ
T i(0);
int i=0;
そのため、一般的なコンストラクター パターンのように見えないの代替として追加されました。これは、テンプレートを使用する場合に便利です。したがって、テンプレートはクラスだけでなく int も使用できます。
new
ヒープメモリの割り当て(リークする可能性がある)を意味するので、いいえ、常にそれをしたくありません。
int i=0;
とint i(0);
は同等ですが、実装によっては、最初のものは代入演算子を使用し、2番目のものは値で構築される場合があります。これにより、コンパイラはターゲット アーキテクチャに合わせて最適化できます。クラスの場合、(通常はデフォルト値で) クラスを作成してから割り当てを実行し、割り当てに時間をかけたすべてのデフォルト値を消去するため、割り当てメソッドはおそらく遅くなります。
誰かが声をかけて、より正確な答えを得るために言語仕様を参照するかもしれません。