3

ポイント先 (->) 演算子とドット (.) 演算子の違いは知っていますが、なぜ 2 つの演算子が必要なのかわかりません。ポインターを使用せずにドット演算子のみを使用することは、常に同じくらい簡単ではありませんか? http://www.programcreek.com/2011/01/an-example-of-c-dot-and-arrow-usage/から

    #include <iostream>

    using namespace std;

    class Car
    {
    public:
int number;

void Create() 
{
    cout << "Car created, number is:  " << number << "\n" ;
}
    };

    int main() {
Car x;
// declares x to be a Car object value,
// initialized using the default constructor
// this very different with Java syntax Car x = new Car();
x.number = 123;
x.Create();

Car *y; // declare y as a pointer which points to a Car object
y = &x; // assign x's address to the pointer y
(*y).Create(); // *y is object
y->Create();

y->number = 456; // this is equal to (*y).number = 456;
y->Create();
    }

なぜポインターをわざわざ使うのでしょうか? X と同じように Y を作成するだけで、同じように機能します。動的に割り当てられたメモリへのポインターが必要だと言うなら、ドット演算子をわざわざ使う必要はありません。

4

7 に答える 7

6

あなたは2つの別々の懸念を混ぜていると思います。

まず、->演算子は不要です。はい。x->yは と同等です(*x).yが、->演算子の方が入力しやすいので、基本的には便利です。

2 番目の部分は、ポインタを使用するかどうかです。多くの場合、そうすべきではありません。デフォルトでは、その場でオブジェクトを作成し、それらを直接参照します。

Foo bar;
bar.baz():

しかし、多くの場合、ポインタは依然として必要です。オブジェクトは他のオブジェクトを参照できる必要があります。参照はそれを行うことができますが、再配置することはできません。初期化されると、常に同じオブジェクトを指します。

ポインターは、別のオブジェクトを指すように更新できます。

リンクされたリスト、ツリー データ構造、およびその他の数え切れないほどのものは、オブジェクトが他のオブジェクトを指すことができることに依存しています。

そうです、ポインタが必要です。しかし、演算子は必要ありません。->便利だから使ってるだけ。

于 2012-04-08T10:33:00.397 に答える
2

を。型を見たり、 のような特別な表記法を使用したりせずに、コードを意味的に理解しやすくするだけですm_pszMyName。コードを読むと、何がポインタで何が値かすぐにわかります。

b. shared_ptr とオーバーライド演算子の場合を考えてみてください。shared_ptr<T>->get()以外の何かを意味しますshared_ptr<T>.get()。1 つ目は指定されたオブジェクト内の関数であり、2 つ目は shared_ptr クラス自体の関数です。これはほんの一例ですが、要点がわかります。

于 2012-04-08T10:24:43.687 に答える
1

あなたのリンクから:

次の例は良い例です。

実際には少し混乱します。Car x;スタック上にオブジェクトを作成し ( )、それへのポインタを作成して を使用してアクセスするのはなぜでしょう->か?

「なぜポインターが必要なのか」という暗黙の質問に答えようとする代わりに、その例から生じたかもしれない混乱を解消しようとします。

あなたのコメントであなたは言う:

異なる方法で作成されたオブジェクト間に違いがあるかどうか疑問に思っています。

この例では、によって作成されたスタックCar上に1 つのオブジェクトしかありません (完全にするために、 によって作成されたスタック上に -pointerもあります)。終了時にスコープ外になるため、メモリがクリーンアップされます。Car x;CarCar *y;main()

しかし、オブジェクトを作成する別の方法があります。これは、コメントに基づいて既に知っていると思います。これは、ヒープnew上でオブジェクトを初期化するために使用します: . ヒープ上のオブジェクトはスコープ外に出ることはないため、呼び出された関数が終了した後もそれらを使用し続けることができますが、メモリ リークを防ぐために使用して明示的にクリーンアップする必要があります。Car *z = new Car;newdelete

オブジェクトへのポインターのより現実的な使用法: の戻り値ですnew

于 2012-04-08T11:05:34.600 に答える
0

->は略語です。クラスが木のノードを表すと考えてください。

struct node {
    int data;
    node* left;
    node* right;
};

メンバーleftは、ノードの左の子へのポインターポイントです。あるノードへのポインタがあると仮定しますp。ここで、pの左の子の左の子の右の子へのポインタポイントを取得したいとします。ドットを使用して、書き込み(*(*(*p).left).left).rightが難しく、読みにくく、エラーが発生しやすく、->を使用します。p->left->left->right非常に明確に書くことができます。

于 2012-04-08T11:09:44.393 に答える
0

C++に両方の->and.演算子が存在することは、C からの直接的な影響です。C では、ポインターを介してオブジェクトにアクセスすることと、現在のスコープで宣言されているオブジェクトにアクセスすることを区別しています。

C++ では、参照は、ローカル スコープのオブジェクトへのアクセスの自然な拡張です。

C の作成者がこれを考慮したかどうかはわかりませんが、私は常に小さな最適化ガイドとして使用していました。コードの一部を見ると、->実行時に最終的なアドレスを計算するのに対し、.オペレーターはコンパイル時にアドレスを計算することがわかります。これは、構造体のメンバーにアクセスする場合でも機能します。次の点を考慮してくださいmyptr->mMember.mValue。mMember から mValue へのオフセットはコンパイル時に計算できますが、ポインターからの最終的なアドレス計算は実行時に計算する必要があります。最近の最適化に関する小さな考慮事項は認めます。C++ での参照では、これを行うことはできなくなりましたが、20 年前は心に留めておくべきことでした。

于 2012-04-08T11:22:53.267 に答える
0

はい、いつでも(*x).memberの代わりに使用できますが、 が複雑な式であるx->member場合に本当に使用したいですか?x

関連するもの (この場合) を遠くに置くと、ソース コードが読みにくくなるため、*「1 つの場所に」置くことは単純に構文として優れています。.->


ポインターの概念の必要性については、主に 2 つの理由があります。

1. オブジェクトの有効期間

オブジェクトを割り当てるには 2 つの方法があります

  • スタック上。
  • 動的メモリ内。

スタックは、実行フローが関数に出入りするときに巻き戻したり巻き戻したりするため、スタック オブジェクトの寿命は、それを作成した関数にとどまる時間と必然的に結びつきます。

オブジェクトを作成した関数よりも長く存続するオブジェクトが必要な場合は、動的メモリに作成する必要があります。そのようなオブジェクトを識別する唯一の方法は、そのメモリ アドレス (別名) を使用することです。ポインター。

2. オブジェクト共有

オブジェクトにアクセスする必要がある他のオブジェクトが複数ありますか? はいの場合、アドレスを保持する以外に、これらののオブジェクトが共有オブジェクトを参照する方法はありません。

他のオブジェクトが 1 つしかない場合でも、それらの有効期間が異なる場合は、「有効期間」の理由が適用されます。他のオブジェクトが 1 つしかなくそれらのライフタイムが一致する場合は、それをフィールドにします。

于 2012-04-08T11:59:54.320 に答える
0

ポインターを使用せずにドット演算子のみを使用することは、常に同じくらい簡単ではありませんか?

C/C++ は、他の高次言語と同様に、ポインターをいくつかのシュガー コーティング構文でカプセル化していません。ポインターは自然に発生するものであり、以下のリストはすべてを網羅しているわけではありません

  1. ヒープからメモリを割り当てます。静的なデータの割り当て、またはスタック内のストレージの割り当ては、常に実現可能ではありません。所有権の譲渡、スペースの制約、およびプログラムの動的な性質に伴うオーバーヘッドがあります。
  2. ファイルの読み取りと書き込み。
  3. C-Type 文字列を含むオブジェクトを繰り返します。配列アクセス構文を使用できますが、安全性の違いはほとんどなく、関数に渡すと配列がポインターに縮退されます (サイズ情報が失われます)。

C++ の観点から見ると、上記のすべてをオブジェクトにカプセル化できます。

  1. iotream による FILE IO
  2. スマート ポインターによるポインター (一部は C++98 から、一部は C++11 または eve boost から)
  3. STL 型オブジェクトの反復子。
  4. 参照の使用

それにもかかわらず、ポインターは、明示的に表示されない言語でも存在します。それらは高次のオブジェクトにカプセル化されているだけです。

これは、ポインター以外のことを考えられない理由をある程度説明しています。おそらく次に興味があるのは構文です。ptr->somememberの代わりに必要なのはなぜですか(*ptr).somemember。繰り返し使用するための省略形です。C/C++ プログラマーはそれに慣れており、余分な構文を使用する単一のプログラムを今日まで見たことがありません。

于 2012-04-08T10:29:20.777 に答える