1

の違いは何ですか

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)ですか?

4

4 に答える 4

5

最初に、少し異なる質問に答えます。

次のようなclassor 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から直接構築することを許可しています。int0

Foo* i = new Foo;

これはフリー ストア (通常は実装され、ヒープと呼ばれます) に移動し、 を格納するのに十分なメモリを取得してからFoo、デフォルトでそれを構築します。次に、このオブジェクトのアドレスを返します。このアドレスはFoo、ポインタの初期化に使用されますFoo* i。ここで、デフォルトのコンストラクターが uninitialized のままであることに注意しFoovalue ください。これはFoo上記の私の実装の欠陥です (私の意見では)。特別な場合を除いて、これを行うことはめったにありません。

厄介なことに、整数 ( charintlongunsigned 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::vectorshared_ptrmake_sharedunique_ptrmake_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()intxint 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)をまだしていないので、かなり厄介です。

于 2013-03-12T02:04:17.453 に答える
2
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++ の本を確認してください。

于 2013-03-12T02:05:09.963 に答える
2

C および初期の C++ では、int i=0; しか使用できませんでした。

パターンは一般的な型のint i(0);コンストラクタと同じ

T i(0);

int i=0;そのため、一般的なコンストラクター パターンのように見えないの代替として追加されました。これは、テンプレートを使用する場合に便利です。したがって、テンプレートはクラスだけでなく int も使用できます。

于 2013-03-12T01:58:48.640 に答える
-1

newヒープメモリの割り当て(リークする可能性がある)を意味するので、いいえ、常にそれをしたくありません。

int i=0;int i(0);は同等ですが、実装によっては、最初のものは代入演算子を使用し、2番目のものは値で構築される場合があります。これにより、コンパイラはターゲット アーキテクチャに合わせて最適化できます。クラスの場合、(通常はデフォルト値で) クラスを作成してから割り当てを実行し、割り当てに時間をかけたすべてのデフォルト値を消去するため、割り当てメソッドはおそらく遅くなります。

誰かが声をかけて、より正確な答えを得るために言語仕様を参照するかもしれません。

于 2013-03-12T01:54:22.360 に答える