C++のアロー演算子とは、-> のことです。
主に、ポインタが指しているオブジェクトのメンバにアクセスするための演算子として使われます。
C++では、オブジェクトそのものからメンバへアクセスする場合と、ポインタを通じてメンバへアクセスする場合で、使う演算子が異なります。
オブジェクトそのものに対してはドット演算子を使い、ポインタに対してはアロー演算子を使います。
つまり、アロー演算子は「ポインタの先にあるオブジェクトのメンバを使いたいとき」に登場する演算子です。
アロー演算子の基本的な考え方
アロー演算子を理解するうえで重要なのは、ポインタが「オブジェクトそのもの」ではなく、「オブジェクトがある場所を指しているもの」だという点です。
通常のオブジェクトであれば、そのオブジェクトが持っているメンバに直接アクセスできます。
一方、ポインタはオブジェクトのアドレスを持っているだけです。
そのため、ポインタを通じてメンバにアクセスする場合は、「そのポインタが指している先を見る」という処理が必要になります。
アロー演算子は、この「ポインタの指す先を見る」と「その先のメンバにアクセスする」という処理をまとめて行うための書き方です。
ポインタの先のメンバにアクセスする
アロー演算子は、ポインタが指しているオブジェクトのメンバ変数やメンバ関数にアクセスするときに使います。
たとえば、あるポインタが人物情報を表すオブジェクトを指している場合、その人物の名前や年齢などのメンバへアクセスするためにアロー演算子を使います。
このときのイメージは、次のようなものです。
ポインタ自体に名前や年齢があるわけではありません。
ポインタが指している先のオブジェクトに、名前や年齢といったメンバがあります。
そのため、アロー演算子は「ポインタが指している先のメンバ」という意味で理解するとわかりやすいです。
アロー演算子は省略形として理解できる
生ポインタの場合、アロー演算子は、ポインタを間接参照してからメンバにアクセスする書き方の省略形として理解できます。
つまり、アロー演算子は単に記号として覚えるのではなく、「ポインタの先にあるオブジェクトを取り出し、そのメンバにアクセスしている」と考えると理解しやすくなります。
この考え方を押さえておくと、アロー演算子がなぜ必要なのか、ドット演算子と何が違うのかも整理しやすくなります。
ドット演算子との違い
C++でメンバにアクセスするときによく使う演算子には、ドット演算子とアロー演算子があります。
この2つの違いは、対象がオブジェクトそのものか、オブジェクトへのポインタかという点です。
オブジェクトそのものに対してはドット演算子を使います。
オブジェクトへのポインタに対してはアロー演算子を使います。
オブジェクトにはドット演算子を使う
変数がオブジェクトそのものである場合、そのメンバへアクセスするにはドット演算子を使います。
この場合、変数自体が実体を表しているため、わざわざポインタの先をたどる必要がありません。
そのため、オブジェクトからメンバ変数やメンバ関数を使うときは、ドット演算子を使うのが自然です。
ポインタにはアロー演算子を使う
一方で、変数がポインタである場合、その変数はオブジェクトそのものではなく、オブジェクトの場所を指しています。
そのため、メンバにアクセスするには、まずポインタの指す先を見る必要があります。
アロー演算子は、この処理を簡潔に書くための演算子です。
初心者のうちは、次のように覚えるとわかりやすいです。
オブジェクトならドット演算子、ポインタならアロー演算子です。
この使い分けが、アロー演算子を理解するうえで最も基本的なポイントです。
アロー演算子を使う場面
アロー演算子は、C++のさまざまな場面で使われます。
特に、ポインタ、スマートポインタ、イテレータ、クラス内の this などでよく登場します。
生ポインタを使う場面
最も基本的なのは、生ポインタを使う場面です。
生ポインタとは、オブジェクトのアドレスをそのまま扱うポインタのことです。
あるオブジェクトのアドレスをポインタに格納し、そのポインタを通じてオブジェクトのメンバにアクセスするとき、アロー演算子を使います。
この使い方が、アロー演算子の基本形です。
動的に作成したオブジェクトを扱う場面
C++では、オブジェクトを動的に作成することがあります。
動的に作成したオブジェクトは、ポインタを通じて扱うことが多いため、メンバにアクセスする際にアロー演算子を使います。
ただし、現代的なC++では、メモリ管理を安全に行うために、生のポインタを直接使うよりも、スマートポインタを使うことが多くなっています。
そのため、アロー演算子は単に昔ながらのポインタ操作だけでなく、スマートポインタを使う場面でも重要です。
スマートポインタを使う場面
スマートポインタは、ポインタのようにオブジェクトを指しながら、メモリ管理を自動化してくれる仕組みです。
代表的なものに、所有権を一つだけ持つスマートポインタや、複数の場所で共有できるスマートポインタがあります。
スマートポインタは通常のポインタとは異なるクラス型ですが、ポインタのように使えるように設計されています。
そのため、スマートポインタでもアロー演算子を使って、管理しているオブジェクトのメンバにアクセスできます。
ただし、厳密には、生ポインタのアロー演算子とは内部の仕組みが少し異なります。
スマートポインタでは、アロー演算子の動作を実現するために、専用の演算子が定義されています。
初心者向けには、「スマートポインタもポインタのようにアロー演算子でメンバにアクセスできる」と理解しておけば十分です。
イテレータを使う場面
C++のコンテナを扱うときには、イテレータという仕組みがよく使われます。
イテレータは、コンテナの中の要素を順番に指し示すためのものです。
イテレータはポインタそのものとは限りませんが、ポインタに似た使い方ができるように設計されています。
そのため、イテレータが指している要素のメンバやメンバ関数にアクセスする場合にも、アロー演算子が使われることがあります。
この場合も、「イテレータが指している先の要素のメンバにアクセスしている」と考えると理解しやすいです。
this とアロー演算子
C++のクラス内では、this という特別なポインタが使えます。
this は、現在のオブジェクト自身を指すポインタです。
つまり、クラスのメンバ関数の中で this を使うと、「今このメンバ関数を呼び出しているオブジェクト」を指すことができます。
this は現在のオブジェクトを指すポインタ
this はオブジェクトそのものではなく、現在のオブジェクトへのポインタです。
そのため、this を通じてメンバにアクセスする場合は、アロー演算子を使います。
この点は、アロー演算子を理解するうえでとても重要です。
this はポインタなので、ドット演算子ではなくアロー演算子を使う、という関係になります。
メンバ変数と引数名を区別できる
this とアロー演算子は、メンバ変数と引数名が同じときにもよく使われます。
たとえば、クラスのメンバ変数とメンバ関数の引数に同じ名前を使った場合、そのままだとどちらを指しているのかがわかりにくくなります。
そのようなときに、this を使うと「このオブジェクトのメンバ変数」を明示できます。
つまり、this とアロー演算子を使うことで、メンバ変数とローカル変数、または引数を区別しやすくなります。
this は省略できることも多い
クラスのメンバ関数内では、多くの場合、this を明示しなくてもメンバ変数やメンバ関数にアクセスできます。
そのため、通常のメンバ関数では、わざわざ this を書かないことも多いです。
ただし、メンバ変数と引数名を区別したい場合や、テンプレートを使った発展的なコードでは、this を明示することがあります。
初心者のうちは、「this は現在のオブジェクトを指すポインタであり、this 経由でメンバにアクセスするときはアロー演算子を使う」と理解しておけば十分です。
アロー演算子を使うときの注意点
アロー演算子は便利ですが、ポインタを扱う演算子である以上、注意すべき点もあります。
特に重要なのは、ポインタが有効なオブジェクトを指しているかどうかです。
何も指していないポインタには使えない
ポインタが何も指していない状態でアロー演算子を使うと、非常に危険です。
何も指していないポインタに対してメンバアクセスを行うということは、存在しないオブジェクトのメンバを使おうとしているのと同じです。
このような操作は、プログラムのクラッシュや予期しない動作につながる可能性があります。
そのため、ポインタが有効かどうか不明な場合は、アロー演算子を使う前に確認する必要があります。
初期化していないポインタにも使えない
初期化していないポインタにアロー演算子を使うのも危険です。
初期化されていないポインタは、どこを指しているかわかりません。
その状態でメンバにアクセスしようとすると、不正なメモリにアクセスしてしまう可能性があります。
ポインタを使う場合は、必ず有効なオブジェクトを指すように初期化してから使うことが大切です。
寿命が終わったオブジェクトにも注意が必要
ポインタが一見有効に見えても、その指しているオブジェクトの寿命がすでに終わっている場合があります。
このようなポインタをダングリングポインタと呼びます。
ダングリングポインタに対してアロー演算子を使うと、すでに存在しないオブジェクトのメンバにアクセスすることになり、未定義動作を引き起こします。
そのため、ポインタを扱うときは、ポインタそのものだけでなく、指しているオブジェクトの寿命にも注意が必要です。
アクセス権は通常どおり適用される
アロー演算子を使えば、どのメンバにも自由にアクセスできるわけではありません。
C++には、公開メンバ、非公開メンバ、保護メンバといったアクセス制御があります。
たとえポインタを通じてアクセスしていても、非公開メンバには外部から直接アクセスできません。
つまり、アロー演算子はあくまで「ポインタ経由でメンバにアクセスするための演算子」であり、アクセス制御を無視できるものではありません。
非公開メンバを操作したい場合は、クラスが用意している公開メンバ関数を通じて操作する必要があります。
const の制約にも注意する
ポインタが指している先が変更不可のオブジェクトである場合、アロー演算子を使っても、そのオブジェクトの内容を自由に変更することはできません。
変更不可のオブジェクトに対しては、変更を伴わないメンバ関数の呼び出しなどに限られます。
つまり、アロー演算子が使えることと、メンバを変更できることは別の話です。
ポインタの型に const が関係している場合は、その制約を意識する必要があります。
アロー演算子と演算子の優先順位
アロー演算子を理解するときには、演算子の優先順位も関係します。
特に、ポインタを間接参照してからメンバにアクセスする書き方では、括弧が重要になります。
間接参照とメンバアクセスの順番
ポインタの先にあるオブジェクトのメンバへアクセスするには、まずポインタを間接参照し、その結果得られたオブジェクトのメンバにアクセスする必要があります。
しかし、C++ではメンバアクセスの演算子の方が、間接参照よりも優先順位が高くなります。
そのため、意図した順番で処理させるには、括弧を使って「先にポインタを間接参照する」ことを明示する必要があります。
アロー演算子は、このような面倒な書き方を避けるためにも便利です。
アロー演算子を使うと読みやすくなる
生ポインタの場合、アロー演算子を使わなくても、ポインタを間接参照してからドット演算子でメンバにアクセスすることはできます。
しかし、その書き方は括弧が必要になり、少し読みにくくなります。
アロー演算子を使えば、「ポインタの先のメンバにアクセスしている」という意図を簡潔に表現できます。
そのため、実際のC++コードでは、ポインタ経由でメンバにアクセスする場合、アロー演算子を使うのが一般的です。
アロー演算子のオーバーロード
C++では、アロー演算子の動作をクラスで定義することもできます。
これを、アロー演算子のオーバーロードといいます。
独自クラスをポインタのように扱える
アロー演算子をオーバーロードすると、独自に作成したクラスをポインタのように扱えるようになります。
たとえば、スマートポインタのようなクラスでは、内部でオブジェクトへのポインタを管理しつつ、外部からは通常のポインタのようにメンバへアクセスできるように設計されています。
このような仕組みによって、利用者は複雑な内部構造を意識せず、アロー演算子を使って自然にオブジェクトのメンバへアクセスできます。
スマートポインタでも使われている考え方
スマートポインタでアロー演算子が使えるのは、アロー演算子に相当する動作が定義されているためです。
そのため、スマートポインタは通常のクラス型でありながら、ポインタのような操作感を持っています。
これは、C++の演算子オーバーロードの代表的な活用例の一つです。
初心者はまず基本の使い方を押さえる
アロー演算子のオーバーロードは発展的な内容です。
初心者の段階では、まず「ポインタが指す先のメンバにアクセスするために使う」という基本を理解することが大切です。
そのうえで、スマートポインタやイテレータを学ぶと、アロー演算子がより広い場面で使われていることが理解しやすくなります。
よくある間違い
アロー演算子でつまずきやすいポイントは、使う対象を間違えることです。
特に、オブジェクトとポインタの区別が曖昧なうちは、ドット演算子とアロー演算子を混同しやすくなります。
オブジェクトにアロー演算子を使ってしまう
オブジェクトそのものに対して、アロー演算子を使うことはできません。
アロー演算子は、ポインタやポインタのように振る舞うものに対して使う演算子です。
そのため、変数がオブジェクトそのものである場合は、ドット演算子を使う必要があります。
ポインタにドット演算子を使ってしまう
逆に、ポインタに対してドット演算子を使ってしまうのもよくある間違いです。
ポインタはオブジェクトそのものではなく、オブジェクトのアドレスを保持しているものです。
そのため、ポインタが指しているオブジェクトのメンバにアクセスするには、アロー演算子を使います。
ポインタが有効か確認せずに使ってしまう
ポインタが有効なオブジェクトを指しているか確認せずにアロー演算子を使うのも、よくある危険な間違いです。
特に、何も指していないポインタ、初期化していないポインタ、寿命が終わったオブジェクトを指すポインタには注意が必要です。
このようなポインタに対してアロー演算子を使うと、プログラムの動作が不安定になります。
アロー演算子を理解するコツ
アロー演算子は、最初は少しわかりにくく感じるかもしれません。
しかし、考え方を整理すれば、それほど難しい演算子ではありません。
「ポインタの先」と考える
アロー演算子を見たら、まず「これはポインタの先にあるメンバへアクセスしている」と考えるとよいです。
アロー演算子の左側にあるものは、基本的にはオブジェクトそのものではなく、オブジェクトを指しているものです。
その指している先にあるメンバを使うために、アロー演算子が使われています。
ドット演算子との使い分けで覚える
アロー演算子だけを単独で覚えるより、ドット演算子との違いで覚えた方が理解しやすくなります。
オブジェクトそのものならドット演算子。
ポインタならアロー演算子。
この使い分けを最初にしっかり覚えると、多くのコードが読みやすくなります。
スマートポインタやイテレータでは「ポインタのようなもの」と考える
スマートポインタやイテレータは、生ポインタとは異なる型です。
しかし、ポインタのようにオブジェクトや要素を指し示すため、アロー演算子が使える場合があります。
このような場面では、「実際のポインタではないが、ポインタのように振る舞うもの」と考えると理解しやすいです。
まとめ
C++のアロー演算子は、ポインタが指しているオブジェクトのメンバにアクセスするための演算子です。
オブジェクトそのものに対してはドット演算子を使い、オブジェクトへのポインタに対してはアロー演算子を使います。
生ポインタの場合、アロー演算子は、ポインタを間接参照してからメンバにアクセスする書き方を簡潔にしたものとして理解できます。
また、スマートポインタやイテレータのように、ポインタのように振る舞う型でもアロー演算子が使われることがあります。
ただし、アロー演算子を安全に使うには、ポインタが有効なオブジェクトを指していることが重要です。
何も指していないポインタ、初期化されていないポインタ、寿命が終わったオブジェクトを指すポインタに対して使うと、危険な動作につながります。
アロー演算子を理解するうえで最も大切なのは、次の考え方です。
オブジェクトそのものならドット演算子、ポインタならアロー演算子。
この基本を押さえておけば、C++のコードでアロー演算子が出てきたときに、「ポインタやポインタのようなものを通じて、メンバにアクセスしている」と読み取れるようになります。
以上、C++のアロー演算子についてでした。
最後までお読みいただき、ありがとうございました。
