演算子のリロード(一般、リレーションシップ、論理、単一、矢印オペレータ)

12930 ワード

c++の大きな特性はリロード(overload)であり、リロードによって機能が似ているいくつかの関数を1つにまとめることができ、プログラムをより簡潔で効率的にすることができる.c++では関数だけでなく演算子もリロードできます.一般的なデータ型間の演算子はリロードする必要がないため、演算子のリロードは主にオブジェクト間向けです.
1.一般演算子のリロード
オブジェクト間の演算を行う場合、プログラムは演算子に対応する関数を呼び出して処理するので、演算子の再ロードにはメンバー関数と友元関数の2つの方法があります.メンバー関数の形式は簡単です.クラスにオペレータに関連する関数を定義します.友元関数はthisポインタがないので、パラメータが1つ増えます.

# include
class A
{
public:
    A(int d):data(d){}
    A operator+(A&);//    
    A operator-(A&);
    A operator*(A&);
    A operator/(A&);
    A operator%(A&);
    friend A operator+(A&,A&);//    
    friend A operator-(A&,A&);
    friend A operator*(A&,A&);
    friend A operator/(A&,A&);
    friend A operator%(A&,A&);
private:
    int data;
};

//       
A A::operator+(A &a)
{
 return A(data+a.data);
}

A A::operator-(A &a)
{
 return A(data-a.data);
}

A A::operator*(A &a)
{
 return A(data*a.data);
}

A A::operator/(A &a)
{
 return A(data/a.data);
}

A A::operator%(A &a)
{
 return A(data%a.data);
}

//       
A operator+(A &a1,A &a2)
{
 return A(a1.data+a2.data);
}

A operator-(A &a1,A &a2)
{
 return A(a1.data-a2.data);
}

A operator*(A &a1,A &a2)
{
 return A(a1.data*a2.data);
}

A operator/(A &a1,A &a2)
{
 return A(a1.data/a2.data);
}

A operator%(A &a1,A &a2)
{
 return A(a1.data%a2.data);
}

//              +、-、*、/ 。
void main(void)
{
 A a1(1),a2(2),a3(3);
 a1=a2+a3;
 //  
 a1=a2.operator+(a3);
}

注意:a 2+a 3を行うときにエラーが発生します.私たちは上で+に対して2つの方法を定義したので、1つを削除すればいいです.
2.関係演算子の再ロード
関数体は簡単なので、後でメンバー関数形式の関数宣言だけを与えます.関係演算子には==,!=,,<=,>=があります.
bool operator == (const A& ); 
bool operator != (const A& );
bool operator < (const A& );
bool operator <= (const A& );
bool operator > (const A& );
bool operator >= (const A& );

3.論理演算子のリロード
bool operator || (const A& );
bool operator && (const A& );
bool operator ! ();

4.単項演算子の再ロード
ここの+、-は正負の意味で、対象の前に置きます.
A& operator + ();
A& operator - ();
A* operator & ();
A& operator * ();

5.自増減演算子の再ロード
++と–場所によっては4つのケースがあり、いずれもリロードできます.
A& operator ++ ();//  ++
A operator ++ (int);//  ++
A& operator --();//  --
A operator -- (int);//  --

6.ビット演算子のリロード
ビット操作
A operator | (const A& );
A operator & (const A& );
A operator ^ (const A& );
A operator << (int i);
A operator >> (int i);
A operator ~ ();

7.代入演算子の再ロード
いいえ=ですよ.
A& operator += (const A& );
A& operator -= (const A& ); 
A& operator *= (const A& );
A& operator /= (const A& );
A& operator %= (const A& );
A& operator &= (const A& );
A& operator |= (const A& );
A& operator ^= (const A& );
A& operator <<= (int i);
A& operator >>= (int i);

8.メモリ演算子のリロード
void *operator new(size_t size);
void *operator new(size_t size, int i);
void *operator new[](size_t size);
void operator delete(void*p);
void operator delete(void*p, int i, int j);
void operator delete [](void* p);

9.矢印オペレータの再ロード
矢印オペレータは、オブジェクトとメンバー名を受け入れ、オブジェクトを参照解除してメンバーを取得する2元オペレータのように見えます.実は矢印オペレータは1元オペレータで、パラメータは表示されません(クラスメンバーで、唯一の暗黙的なパラメータはthisまたは*this).の右オペランドは式ではなく、クラスメンバーに対応する識別子であり、コンパイラがメンバーの取得作業を処理します(コンパイラがリロード矢印オペレータに対して行うことは、他のリロードオペレータよりも多く、ここでも複雑な場所です).
矢印オペレータはユニークです.外観にかかわらず、矢印オペレータは明示的なパラメータを受け入れません.
実際には、->の右操作数は式ではなく、逆にクラスメンバーに対応する識別子であるため、2番目のパラメータはありません.識別子をパラメータとして関数に渡す方法は明らかではありません.逆に、コンパイラがメンバーを取得する作業を処理します.
次のように記述します.
 point->action();

優先度ルールは、実際には記述と同等です.
 (point->action)();

すなわち,point->actionの評価結果を呼び出したい.コンパイラはこのようにコードを評価します.
  • pointがactionというメンバーを持つクラスオブジェクトを指すポインタである場合、コンパイラはオブジェクトを呼び出すactionメンバーにコードをコンパイルします.
  • それ以外の場合、pointがoperator->オペレータのクラスを定義(リロード)したオブジェクト(ポインタではない)の場合、
  • point->action   point.operator->()->action   。

    すなわち,pointのoperator->()を実行し,その結果を用いてこの3つのステップを繰り返す.
  • それ以外の場合、コードにエラーが発生します.

  • リロード矢印の戻り値に対する制約
    リロード矢印オペレータは、クラスタイプ(オブジェクト)を指すポインタを返すか、独自の矢印オペレータを定義したクラスタイプオブジェクトを返す必要があります.
    戻りタイプがポインタの場合、コンパイラはポインタを参照解除し、結果オブジェクトから指定したメンバーを取得するために内蔵矢印オペレータを使用できます.指定されたタイプがそのメンバーを定義していない場合、コンパイラはエラーを発生します.
    戻りタイプがクラスタイプの他のオブジェクト(またはそのオブジェクトの参照)である場合、オペレータは再帰的に適用されます.コンパイラは、返されるオブジェクトが属するタイプにメンバー矢印があるかどうかを確認し、ある場合はそのオペレータを適用します.そうでなければ、コンパイラにエラーが発生します.このプロシージャは、指定したメンバーを持つオブジェクトへのポインタを返すか、後でコードがエラーになるまで続行します.
    内部ポインタ操作が呼び出されるまで停止しません.
    10.特殊演算子の再ロード
    上の演算子のリロードには2つの方法がありますが、下の演算子は1つしか使えません.特殊でしょう.これらの演算子のリロードはメンバー関数のみです.
    A& operator = (const A& );
    char operator [] (int i);//         
    const char* operator () ();
    T operator -> ();
    //     
    operator char* () const;
    operator int ();
    operator const char () const;
    operator short int () const;
    operator long long () const;
    //        

    これらは友元関数としてのみ再ロードできます
    friend inline ostream &operator << (ostream&, A&);//   
    friend inline istream &operator >> (istream&, A&);//   

    11.まとめ
    2つのリロード方式の比較:一般的に、単一演算子はクラスのメンバー関数としてリロードすることが望ましい.両目演算子は、クラスの友元関数として再ロードすることが望ましい.次の両目演算子では、=、()、[]、->のようにクラスの友元関数として再ロードできません.タイプ変換関数は、クラスのメンバー関数としてのみ定義でき、クラスの友元関数として定義できません.C++は4種類の変換関数を提供する:reinterpret_cast(コンパイル中に変換を実現)、const_キャスト(コンパイル中に変換)、stactic_cast(コンパイル中に変換を実現)、dynamic_cast(実行中に変換が実現され、変換が成功したかどうかのフラグが返されます).演算子の操作でオブジェクトの状態を変更する必要がある場合は、「メンバー関数として再ロード」を選択したほうがいいです.演算子に必要なオペランド(特に最初のオペランド)に暗黙的なタイプ変換が必要な場合は、友元関数のみが選択されます.演算子関数がメンバー関数である場合、一番左のオペランド(または一番左のオペランドのみ)は、演算子クラスのクラスオブジェクト(またはそのクラスオブジェクトへの参照)である必要があります.左側のオペランドが異なるオブジェクト、または内部タイプのオブジェクトである必要がある場合、演算子関数は友元関数として実装する必要があります.リロード演算子に交換性が必要な場合は、「友関数としてリロード」を選択します.注意事項:クラス関係演算子を除く.「、メンバーポインタ演算子」.*「、役割ドメイン演算子」:「、sizeof演算子、および3つの演算子」::「それ以外は、C++のすべての演算子をリロードできます.リロード演算子は、C++言語の既存の演算子の範囲内にあるリロード可能な演算子に制限され、新しい演算子を作成することはできません.演算子リロードは実質的に関数リロードであるため、コンパイラによる演算子リロードの選択は、関数リロードの選択原則に従います.リロード後の演算子は変更できません変演算子の優先度と結合性は,演算子操作数の個数や構文構造を変えることもできない.演算子のリロードでは、内部タイプオブジェクトに使用される演算子の意味を変更することはできません.ユーザー定義タイプのオブジェクトとのみ使用できます.または、ユーザー定義タイプのオブジェクトと内部タイプのオブジェクトを混合して使用する場合にのみ使用できます.演算子のリロードは、新しいタイプのデータの実際の必要性に対して既存の演算子を適切に改造し、リロードの機能は既存の機能と類似し、目的もなくリロード演算子を使用しないようにしなければならない.
    参考文献[1]本文はWuyuan’s Blogから転載し、文章の住所:http://wuyuans.com/2012/09/cpp-operator-overload [2]《C++ Primer》