オペレータリロードの解参照と矢印オペレータ
矢印オペレータはユニークです.オブジェクトとメンバー名を受け入れ、オブジェクトを参照解除してメンバーを取得する2元オペレータのように表現できます.
外観にかかわらず、矢印オペレータは明示的なパラメータを受け入れません.ここには2番目のパラメータはありません.->の右操作数は式ではなく、逆にクラスメンバーに対応する識別子です.
識別子をパラメータとして関数に渡す方法は明らかではありません.逆に、コンパイラがメンバーを取得する作業を処理します.
理解1
このように書くと、point->action();
優先度ルールのため、実際には作成に等しい:(point->action)();
すなわち,point->actionの評価結果を呼び出したい.コンパイラはこのようにコードを評価します.
1.pointがactionというメンバーを持つクラスオブジェクトを指すポインタである場合、コンパイラはオブジェクトを呼び出すactionメンバーにコードをコンパイルします.
2.そうでない場合、pointがoperator->オペレータのクラスを定義したオブジェクトの場合、point->actionはpoint.operator->()->actionと同じです.
つまりpointのoperator->()を実行し、その結果を使用してこの3つのステップを繰り返します.
3.そうでない場合、コードにエラーが発生します.
理解2
矢印オペレータは、一般的に左右の2つの部分があります:a->b;
どのように開始するか:aから始まり、次の2つの状況に分けられます.
1、aはポインタであり、それは私たちがよく知っている、私たちのaタイプのメンバーデータまたは関数「b」を指す.ここまでで、おしまい!
2、aがオブジェクトである場合、aはメンバー関数「operator->」を持つ必要があります(そうでないとエラーが発生します).では、aのoperator->関数を呼び出します.operator->はポインタを返す可能性があるので、
オブジェクトかもしれませんが、ポインタかオブジェクトか、1か2を歩いて、このように再帰します.どのように終了するか:aがポインタまたはオブジェクトであっても、最終的にはポインタが終了するまで歩きます.
理解3
リロード矢印オペレータは、クラスタイプを指すポインタを返すか、独自の矢印オペレータを定義したクラスタイプオブジェクトを返す必要があります.
戻りタイプがポインタの場合、コンパイラはポインタを参照解除し、結果オブジェクトから指定したメンバーを取得するために内蔵矢印オペレータを使用できます.指定されたタイプがそのメンバーを定義していない場合、コンパイラはエラーを発生します.
戻りタイプがクラスタイプの他のオブジェクト(またはそのオブジェクトの参照)である場合、オペレータは再帰的に適用されます.コンパイラは、返されるオブジェクトが属するタイプにメンバー矢印があるかどうかを確認し、ある場合はそのオペレータを適用します.そうでなければ、コンパイラにエラーが発生します.このプロシージャは、指定したメンバーを持つオブジェクトへのポインタを返すか、後でコードがエラーになるまで続行します.
例
I am C
I am D
I am D
I am B
I am A
deference test
I am C
I am D
I am C
I am D
I am D
結果分析
矢印オペレータと参照オペレータは、クラスオブジェクトにのみ再ロードされます.ポインタに作用し,元の原生態ポインタの意味に従って処理する.
1、a->info();
まず、aはオブジェクトであり、operator->があり、呼び出され、C*に戻り、ポインタです.では、次は関数infoを呼び出し、対応するCのメンバー関数を見つけます.
2、a->operator->()->Print();
a「->」で返されるC*を呼び出し、ポインタです.では、次はCを呼び出すメンバー関数です.後ろにoperator->()があり、ちょうどCのメンバー関数です.
Cのoperator->はD*を返し、ポインタであるので、対応するDのメンバー関数info()を呼び出します.
すなわち、「a->operator->()->info()」のoperator->()は、Cのメンバー関数として見る必要がある(ちょうどD*を返し、矢印「->」)を呼び出すことができる)と理解しやすい.
3、b->info();
bはオブジェクトであり、operator->を呼び出してCオブジェクトを返すと、再び再帰する必要がある.Cはオブジェクトであり、operator->を呼び出してD*を返すと、ポインタであり、終了:対応する関数infoを呼び出すことができます.
4、bp->info();
bpはポインタで、次は関数infoを呼び出して、対応するBのメンバー関数を見つけます
5、ap->info();
apはポインタで、次は関数infoを呼び出して、対応するAのメンバー関数を見つけます
解引用分析:
6、 (*da)->info();daはAタイプのオブジェクトであり、(*da)operator*を呼び出してCタイプへのポインタを返し、矢印オペレータ操作規則と同じになる.
7、(*db)->info()と(*db).info();dbはBのオブジェクト(*db)がCタイプのオブジェクトへの参照を返す、すなわち(*db)はCタイプのオブジェクト、->後同矢印オペレータ操作規則である.オペレータは、オブジェクトのメンバーを直接呼び出します.
8、(*dc)->info();dcはCタイプのオブジェクトであり、(*dc)operator*を呼び出してDタイプへのポインタを返し、矢印オペレータ操作規則と同じになります.
上記の例は、参照オペレータと矢印オペレータの解を容易にするためだけであり、実用的な価値はありません.
STLソースでは、operator*はオブジェクトの参照を返し、operator->はオブジェクトのポインタを返します.ここのオブジェクトは、対応するコンテナの要素です.
外観にかかわらず、矢印オペレータは明示的なパラメータを受け入れません.ここには2番目のパラメータはありません.->の右操作数は式ではなく、逆にクラスメンバーに対応する識別子です.
識別子をパラメータとして関数に渡す方法は明らかではありません.逆に、コンパイラがメンバーを取得する作業を処理します.
理解1
このように書くと、point->action();
優先度ルールのため、実際には作成に等しい:(point->action)();
すなわち,point->actionの評価結果を呼び出したい.コンパイラはこのようにコードを評価します.
1.pointがactionというメンバーを持つクラスオブジェクトを指すポインタである場合、コンパイラはオブジェクトを呼び出すactionメンバーにコードをコンパイルします.
2.そうでない場合、pointがoperator->オペレータのクラスを定義したオブジェクトの場合、point->actionはpoint.operator->()->actionと同じです.
つまりpointのoperator->()を実行し、その結果を使用してこの3つのステップを繰り返します.
3.そうでない場合、コードにエラーが発生します.
理解2
矢印オペレータは、一般的に左右の2つの部分があります:a->b;
どのように開始するか:aから始まり、次の2つの状況に分けられます.
1、aはポインタであり、それは私たちがよく知っている、私たちのaタイプのメンバーデータまたは関数「b」を指す.ここまでで、おしまい!
2、aがオブジェクトである場合、aはメンバー関数「operator->」を持つ必要があります(そうでないとエラーが発生します).では、aのoperator->関数を呼び出します.operator->はポインタを返す可能性があるので、
オブジェクトかもしれませんが、ポインタかオブジェクトか、1か2を歩いて、このように再帰します.どのように終了するか:aがポインタまたはオブジェクトであっても、最終的にはポインタが終了するまで歩きます.
理解3
リロード矢印オペレータは、クラスタイプを指すポインタを返すか、独自の矢印オペレータを定義したクラスタイプオブジェクトを返す必要があります.
戻りタイプがポインタの場合、コンパイラはポインタを参照解除し、結果オブジェクトから指定したメンバーを取得するために内蔵矢印オペレータを使用できます.指定されたタイプがそのメンバーを定義していない場合、コンパイラはエラーを発生します.
戻りタイプがクラスタイプの他のオブジェクト(またはそのオブジェクトの参照)である場合、オペレータは再帰的に適用されます.コンパイラは、返されるオブジェクトが属するタイプにメンバー矢印があるかどうかを確認し、ある場合はそのオペレータを適用します.そうでなければ、コンパイラにエラーが発生します.このプロシージャは、指定したメンバーを持つオブジェクトへのポインタを返すか、後でコードがエラーになるまで続行します.
例
#include <iostream>
using namespace std;
class D
{
public:
void info(){cout << "I am D" << endl;}
};
class C
{
public:
void info(){cout << "I am C" << endl;}
D* operator*(){return &m_d;}
D* operator->(){return &m_d;}
private:
D m_d;
};
class B
{
public:
void info(){cout << "I am B" << endl;}
C& operator*(){return m_c;}
C& operator->(){return m_c;}
private:
C m_c;
};
class A
{
public:
void info(){cout << "I am A" << endl;}
C* operator*(){return &m_c;}
C* operator->(){return &m_c;}
private:
C m_c;
};
int main()
{
A a;
a->info();
a->operator->()->info();
B b;
b->info();
B *bp = &b;
bp->info();
A *ap = &a;
ap->info();
cout << endl << "deference test" << endl << endl;
A da;
(*da)->info();
B db;
(*db)->info();
(*db).info();
(**db)->info();
C dc;
(*dc)->info();
return 0;
}
実行結果I am C
I am D
I am D
I am B
I am A
deference test
I am C
I am D
I am C
I am D
I am D
結果分析
矢印オペレータと参照オペレータは、クラスオブジェクトにのみ再ロードされます.ポインタに作用し,元の原生態ポインタの意味に従って処理する.
1、a->info();
まず、aはオブジェクトであり、operator->があり、呼び出され、C*に戻り、ポインタです.では、次は関数infoを呼び出し、対応するCのメンバー関数を見つけます.
2、a->operator->()->Print();
a「->」で返されるC*を呼び出し、ポインタです.では、次はCを呼び出すメンバー関数です.後ろにoperator->()があり、ちょうどCのメンバー関数です.
Cのoperator->はD*を返し、ポインタであるので、対応するDのメンバー関数info()を呼び出します.
すなわち、「a->operator->()->info()」のoperator->()は、Cのメンバー関数として見る必要がある(ちょうどD*を返し、矢印「->」)を呼び出すことができる)と理解しやすい.
3、b->info();
bはオブジェクトであり、operator->を呼び出してCオブジェクトを返すと、再び再帰する必要がある.Cはオブジェクトであり、operator->を呼び出してD*を返すと、ポインタであり、終了:対応する関数infoを呼び出すことができます.
4、bp->info();
bpはポインタで、次は関数infoを呼び出して、対応するBのメンバー関数を見つけます
5、ap->info();
apはポインタで、次は関数infoを呼び出して、対応するAのメンバー関数を見つけます
解引用分析:
6、 (*da)->info();daはAタイプのオブジェクトであり、(*da)operator*を呼び出してCタイプへのポインタを返し、矢印オペレータ操作規則と同じになる.
7、(*db)->info()と(*db).info();dbはBのオブジェクト(*db)がCタイプのオブジェクトへの参照を返す、すなわち(*db)はCタイプのオブジェクト、->後同矢印オペレータ操作規則である.オペレータは、オブジェクトのメンバーを直接呼び出します.
8、(*dc)->info();dcはCタイプのオブジェクトであり、(*dc)operator*を呼び出してDタイプへのポインタを返し、矢印オペレータ操作規則と同じになります.
上記の例は、参照オペレータと矢印オペレータの解を容易にするためだけであり、実用的な価値はありません.
STLソースでは、operator*はオブジェクトの参照を返し、operator->はオブジェクトのポインタを返します.ここのオブジェクトは、対応するコンテナの要素です.