1. ホーム
  2. c++

[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?

2022-03-16 17:32:10

質問

私はJava出身で、C++でオブジェクトを扱うようになりました。しかし、一つ気になったのは、例えばこの宣言のように、人々はしばしばオブジェクトそのものではなく、オブジェクトへのポインタを使用することです。

Object *myObject = new Object;

よりも

Object myObject;

また、関数を使う代わりに、次のようにします。 testFunc() , こんな感じです。

myObject.testFunc();

を書かなければならない。

myObject->testFunc();

しかし、なぜこのようにする必要があるのかが分かりません。メモリアドレスに直接アクセスできるので、効率とスピードに関係しているのだと思います。そうでしょうか?

どのように解決するのですか?

ダイナミックアロケーションを頻繁に目にするのは非常に残念なことです。これは、いかに多くの悪いC++プログラマーがいるかを示しています。

ある意味、2つの疑問が1つにまとまっているんですね。ひとつは、いつダイナミック・アロケーションを使うべきか、ということです。 new )? もう一つは、いつポインターを使うべきか?

重要なメッセージは、次のとおりです。 常に仕事に適したツールを使用する . ほとんどすべての状況において、手動で動的割り当てを行ったり、生のポインタを使用するよりも、より適切で安全な方法があるはずです。

ダイナミックアロケーション

質問では、オブジェクトを作成する2つの方法を示しました。主な違いは、オブジェクトの保存期間です。オブジェクトを作成する際に Object myObject; つまり、オブジェクトはスコープ外に出たときに自動的に破棄されます。ブロック内で new Object() の場合、オブジェクトは動的保存期間を持ち、明示的に delete となります。動的保存期間は必要なときだけ使用するようにしましょう。 つまり を使用する必要があります。 常に 可能であれば、自動保存期間を設定したオブジェクトを作成することをお勧めします。 .

ダイナミックアロケーションが必要となる主な状況は2つ。

  1. オブジェクトが現在のスコープより長持ちする必要がある場合 - その特定のオブジェクトのコピーではなく、その特定のメモリ位置にある特定のオブジェクトのコピーです。もし、オブジェクトのコピー/移動に問題がなければ(ほとんどの場合、そうすべきです)、自動的なオブジェクトを好むはずです。
  2. 大量のメモリを確保する必要がある スタックを埋め尽くすかもしれません。C++の権限外のことなので、これを気にする必要がなければいいのですが(ほとんどの場合、気にする必要はありません)、残念ながら、私たちが開発しているシステムの現実に対処しなければならないのです。

ダイナミックアロケーションが絶対に必要な場合は、スマートポインタなど、ダイナミックアロケーションを実行する型にカプセル化する必要があります。 RAII (標準的なコンテナのように)。スマートポインターは、動的に割り当てられたオブジェクトの所有権に関するセマンティクスを提供します。以下の例を見てください。 std::unique_ptr std::shared_ptr などがあります。これらを適切に使えば、独自のメモリ管理をほとんど行わずに済みます ( ゼロの法則 ).

ポインタ

しかし、生のポインタにはダイナミック・アロケーション以外にももっと一般的な使い方がありますが、ほとんどの場合、好みの代替手段があるはずです。以前と同様に 本当にポインタが必要でない限り、常に代替手段を選ぶようにしましょう。 .

  1. 参照セマンティクスが必要 . オブジェクトを渡す関数がその特定のオブジェクト(コピーではない)にアクセスできるようにしたいため、(どのように割り当てられたかに関係なく)ポインタを使用してオブジェクトを渡したい場合があります。しかし、ほとんどの場合、ポインタよりも参照型の方が適しています。なぜなら、参照型は特にそのために設計されているからです。これは、上記の状況1のように、必ずしも現在のスコープを超えてオブジェクトの寿命を延長することではないことに注意してください。先ほどと同じように、オブジェクトのコピーを渡しても問題ないのであれば、参照セマンティクスは必要ありません。

  2. ポリモーフィズムが必要 . オブジェクトへのポインタや参照を通してのみ、ポリモーフィックに(つまり、オブジェクトの動的型に従って)関数を呼び出すことができます。もし、そのような動作が必要であれば、ポインタか参照を使う必要があります。繰り返しになりますが、リファレンスを使用することをお勧めします。

  3. あるオブジェクトがオプションであることを表現したい を許可することで nullptr を渡すと、そのオブジェクトが省略されます。引数であれば、デフォルト引数や関数のオーバーロードを使うのが望ましいでしょう。そうでない場合は、できればこの動作をカプセル化した型、たとえば std::optional (C++17で導入 - 以前のC++規格では boost::optional ).

  4. コンパイル時間を短縮するために、コンパイル単位を切り離したい。 . ポインタの便利な特性は、指し示す型の前方宣言だけが必要なことです(実際にオブジェクトを使用するには、定義が必要です)。これにより、コンパイル処理の一部を切り離すことができ、コンパイル時間を大幅に短縮できる可能性があります。詳細は Pimplイディオム .

  5. C言語ライブラリとのインターフェイスが必要 またはC言語スタイルのライブラリです。このとき、生ポインタを使わざるを得なくなります。一番良いのは、生ポインタを解放するのは、可能な限り最後の瞬間にすることです。スマートポインタから生ポインタを得るには、例えば、スマートポインタの get メンバ関数です。もしライブラリがあなたに代わって何らかの割り当てを行い、ハンドルを使ってあなたがそれを解除することを期待するなら、あなたはしばしばそのハンドルをスマートポインターでラップし、独自のデレターでオブジェクトを適切に解除することができます。