2

私は現在C++の本を読んでいますが、クラスを指すことについて少し混乱しています。

この本の前半で、例ではクラスとメソッドを次のように使用しました。

Calculator myCalc;
myCalc.launch();

while( myCalc.run() ){
    myCalc.readInput();
    myCalc.writeOutput();
}

ただし、現在は次のように変更されています。

Calculator* myCalc = new Calculator;
myCalc -> launch();

while( myCalc -> run() ){
    myCalc -> readInput();
    myCalc -> writeOutput();
}

そして、なぜそれがこのように行われているのかについての説明をそこに見つけることができないようです。

標準的な方法ではなく、この方法でクラスをポイントしたいのはなぜですか?

違いはなんですか?そして、どちらが好ましいのでしょうか?

ありがとうございました。

4

7 に答える 7

9

まず、クラスを指しているのではなく、オブジェクトとも呼ばれるクラスのインスタンスを指しているのです。(C ++ではクラスを指すことはできません。私に言わせれば、その欠点の1つです)。

違いは、オブジェクトが割り当てられる場所です。あなたがしているとき:

Calculator myCalc;

オブジェクト全体がスタック上に作成されます。スタックは、ローカル変数やネストされた呼び出しなどのストレージであり、多くの場合、1MB以下に制限されています。一方、メモリマネージャの呼び出しが含まれないため、スタックでの割り当ては高速です。

あなたがするとき:

Calculator *myCalc;

ポインタがスタックに割り当てられていることを除いて、あまり起こりません。ポインタは通常4または8バイトのサイズ(32ビットと64ビットのアーキテクチャ)であり、メモリアドレスのみを保持します。次のような操作を行って、オブジェクトを割り当て、ポインタがそのオブジェクトを指すようにする必要があります。

myCalc = new Calculator;

これは、例に示すように1つの行に結合することもできます。ここでは、オブジェクトはヒープに割り当てられます。ヒープは、物理メモリとほぼ同じ大きさであるため(スワップスペースとアーキテクチャの制限は考慮されません)、そこにはるかに多くのデータを格納できます。ただし、メモリマネージャが起動してオブジェクトのヒープ上の予備の場所を見つける必要があるため、またはオペレーティングシステムからより多くのメモリを取得する必要があるため、処理速度は遅くなります。これで、ポインタにオブジェクトのメモリアドレスが含まれるため、および演算子myCalcで使用できます。*->

また、スコープが終了すると(つまり、関数の終了時に)スタックがクリーンアップされ、オブジェクトが使用できなくなるため、スコープ外のスタック上のオブジェクトへのポインターまたは参照を渡すことはできません。

ああ、ほとんど言及するのを忘れていました。ヒープ上のオブジェクトは自動的に破棄されないため、次のように手動で削除する必要があります*:

delete myCalc;

要約すると、スコープを離れない小さな短い生き物の場合は、スタックベースの割り当てを使用できますが、大きくて長い生きているオブジェクトの場合は、通常、ヒープが適しています。


*:ええと、理想的には、そのようではありません。のようなスマートポインタを使用しますstd::unique_ptr

于 2012-07-21T14:35:03.273 に答える
1

どちらも標準です。一方は他方よりも優先されません。

最初のものは、狭い範囲で宣言して使用する典型的なローカル変数です。

ポインタメソッドを使用すると、メモリを動的に割り当てて、ポインタタイプに割り当てることができます。それが「スター」表記の意味です。これらは、メソッドから渡すか、メンバー変数に割り当てて、メソッドが終了した後も存続させることができます。

ただし、ポインタが参照するオブジェクトを使い終わったら、そのメモリをクリーンアップする責任もあることに注意する必要があります。そうしないと、多くの人が最終的に「メモリリーク」を伴う長時間実行アプリケーションを使い果たしてしまいます。

于 2012-07-21T14:29:58.070 に答える
1

変数がクラスのインスタンスまたは参照である場合はドット(。)を使用し、変数がクラスのインスタンスへのポインターである場合は->を使用します。

于 2012-07-21T14:32:07.310 に答える
1

これらは両方ともC++標準の一部ですが、コアの違いがあります。最初の方法では、オブジェクトはスタック上に存在します(ここに関数とローカル変数が格納され、使用されなくなった後に削除されます)。代わりに、変数の型をポインターとして宣言すると、ポインターはスタックに格納されるだけであり、オブジェクト自体はヒープ上にあります。

スタックローカル変数を使用してメモリを割り当てると、C++によって自動的に処理されます。ヒープ上にある場合は、でメモリを取得し、newで解放する必要がありdeleteます。

スタックの例では、コードが.メソッドを呼び出すために使用し、ポインターでメソッドを呼び出すために、C++はショートカットを提供します。->これは。と同等*obj.method()です。

newを使用するときは、常にを使用するdeleteことを忘れないでください。

于 2012-07-21T14:35:04.313 に答える
0

1つの用途は、変数myCalcの有効期間が非常に長い場合です。を使用する場合は必要なときに作成し、を使用する場合はnew削除できますdelete。そうすれば、必要がなく、スペースをとるだけのときに持ち歩くことを心配する必要はありません。または、必要に応じて自由に再初期化することもできます。

または、非常に大きなクラスがある場合newは、スタックではなくヒープに割り当てるために使用するのが一般的な方法です。これは、スタックスペースが少なくヒープが大きかった時代の残り物なので、ヒープスペースが安かったです。

または、もちろん、最も一般的な使用法である動的配列の割り当て。myCalc = new Calculator[x]; x新しい計算機を作成します。事前に大きさがわからない場合は、静的変数を使用してこれを行うことはできませんx。作成するオブジェクトの数。

于 2012-07-21T14:38:16.660 に答える
0

表記法/構文の明らかな違い以外。ポインタは通常、データを関数に渡すときに役立ちます。

void myFunc(Calculator *c) {
    ...
}

通常はよりも優先されます

void myFunc(Calculator c) {
    ...
}

2つ目は、電卓のコピーを作成する必要があるためです。ポインタには、ポイントされている場所への場所のみが含まれるため、データ自体を含むのではなく、メモリ内の別のスポットのみを参照します。別の良い使用法は文字列です。テキストファイルを読み取り、関数を呼び出してテキストを処理することを想像してください。各関数は、ポインタでない場合は文字列のコピーを作成します。ポインタは、マシンアーキテクチャに応じて、4バイトまたは8バイトのいずれかであるため、関数に渡すときに多くの時間とメモリを節約できます。

場合によっては、コピーを使用する方がよい場合もあります。たぶんあなたはそのように変更されたバージョンを返したいだけです

Calculator myFunc(Calculator c) {
    ...
}

ポインタに関する重要なことの1つは、「new」キーワードです。ポインタを作成する唯一の方法ではありませんが、C++の場合よりも簡単な方法です。また、malloc()と呼ばれる関数を使用できるはずですが、それは構造体とc IMOの方が多いですが、私は両方の方法を見てきました。

Cと言えば、ポインタは配列にも適している可能性があります。C ++でもコンパイル時に配列のサイズを宣言することしかできないと思いますが、私は間違っている可能性があります。あなたは私が信じる以下を使うことができます

Calculator *c;
....
Calculator d = c[index];

これで、非常にあいまいなIMOにすることができる配列ができました。

これは私が知っているほぼすべてをカバーしていると思います。提供された例では、提供された2つのスニペットの間に違いはないと思います。

于 2012-07-21T14:45:01.653 に答える
0

まず第一に、あなたはクラスを指しているのではなく、そのクラスのインスタンス(またはオブジェクト)を指しているのです。他のいくつかの言語では、クラスも実際にはオブジェクトです:-)

例はまさにその例です。ほとんどの場合、そこではポインタを使用しません。

さて、ポインタとは何ですか?ポインタは、本物を指す小さなものです。ドアベルの名札のように-それはあなたの名前を示していますが、実際にはあなたではありません。ただし、実際にはあなたではないため、さまざまな場所に自分の名前が付いた複数のボタンを配置できます。

これがポインタを使用する理由の1つです。オブジェクトが1つあるが、そのオブジェクトへのポインタをさまざまな場所に保持したい場合。つまり、現実の世界には、あらゆる場所であなたへの「ポインタ」がたくさんあります。プログラムがデータ内に同様のものを必要とする可能性があることを想像するのはそれほど難しいことではありません。

ポインタは、オブジェクトをコピーする必要がないようにするためにも使用されます。これは、コストのかかる操作になる可能性があります。関数へのポインタを渡す方がはるかに安価です。さらに、関数がオブジェクトを変更できるようにします(技術的には、C ++の「参照」もポインターであり、少しわかりにくく、制限が多いことに注意してください)。

さらに、「new」で割り当てられたオブジェクトは、「delete」で割り当てが解除されるまで残ります。したがって、それらはスコーピングに依存しません-それらはそれらの周りの機能が終了したときに消えることはなく、それらが失われるように言われたときにのみ消えます。

さらに、どのようにして「フルーツの入ったバッグ」を作りますか?「バッグ」オブジェクトを割り当てます。次に、「フルーツ」オブジェクトを割り当て、バッグオブジェクト内にフルーツオブジェクトを指すポインタを設定します。これは、バッグにそのフルーツが含まれていることを示します。果物はバッグオブジェクトへのポインタも取得する可能性があるため、果物で動作するコードもバッグに到達できます。別の「フルーツ」オブジェクトを割り当てて、ポインターのチェーンを確立することもできます。各「フルーツ」には、「次の」フルーツを指す単一の「次の」ポインターを含めることができるため、任意の数のフルーツをバッグに入れることができます。 :バッグには最初の果物へのポインターが含まれ、各果物には別の果物へのポインターが含まれています。だからあなたは果物のチェーン全体を手に入れます。(これは単純な「コンテナ」です。

実際には、ポインタが使用される時期や理由の説明を思いつくのはそれほど簡単ではありません。通常、それらが必要になる状況があります。このような状況に遭遇した場合、それらの有用性を確認するのははるかに簡単です。「なぜ傘が便利なのか」のように、外に降り注ぐ雨に足を踏み入れると、傘の有用性が明らかになります。

于 2012-07-21T14:46:23.597 に答える