【C++】演算子リロードのまとめ

25017 ワード

演算子のリロードの概念と構文
リロードとは、新しい意味を与えることです.演算子のリロードと関数のリロードは似ています.同じ演算子には異なる機能があります.
演算子を再ロードする方法は、関数を定義し、関数内で所望の機能を実現し、その演算子を使用するとコンパイラが自動的にこの関数を呼び出します.すなわち,演算子リロードは関数定義によって実現され,本質的に関数リロードである.
戻り値タイプoperator演算子名(パラメータリスト){//TODO;}
Operatorはキーワードで、リロード演算子を定義するための関数です.
再ロード演算子のルール:
1):             。       (sizeof)、     (: ?)、     (.)、     (.*)、    (::)     。
2):                 。
3):            。  "+"             。
4):                ,               ,      。

次のコードは複数のクラスを定義し、演算子の再ロードによって「+」号で複数の加算を実現します.
/*************************************************************************
    > File Name:      .cpp
    > Author: Tanswer_
    > Mail: [email protected]
    > Created Time: 2016 10 15      19 49 34 
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class Complex
{
private:
    double real;    //  
    double imag;    //  
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}

    Complex operator +(const Complex & A) const; //    +  
    void display() const;
};

/*     */
Complex Complex::operator+(const Complex & A) const
{
    /*     */
    return Complex(real+A.real,imag+A.imag);
    /*    */
    /*Complex B;
    B.real = A.real + real;
    B.imag = A.imag + imag;
    return B;
    */
};



void Complex::display() const 
{
    cout << real << "+" << imag << "i" << endl;
};

int main()
{
    Complex a(1.1,2.2);
    Complex b(3.3,4.4);

    Complex c;
    c = b + a;
    c.display();
    return 0;
}
c=a+bが実行されると、コンパイラは「+」号の左側(左結合性)がComplexクラスオブジェクトであることを検出すると、演算子リロード関数が呼び出され、文はa.operator+(b)に変換されます.
グローバル関数による演算子の再ロード
演算子リロード関数は、クラスのメンバー関数として宣言するか、すべてのクラス以外のグローバル関数として宣言できます.
クラスとして宣言されたメンバー関数
この場合、二元演算子は1つしかありません.一元演算子にはパラメータは必要ありません.パラメータが1つ少ないのは、このパラメータが暗黙的であるためであり、上述したように、コンパイラは、c=a+bを実行すると、「+」号の左側(左結合性)がComplexクラスオブジェクトであることを検出すると、この演算子リロード関数を呼び出し、文は、a.operator+(b)に変換される.このポインタによってaのメンバー変数に暗黙的にアクセスします.
クラス以外のグローバル関数として宣言
演算子のリロード関数をグローバル関数として宣言する場合、2元オペレータには2つのパラメータが必要です.1元オペレータには1つのパラメータが必要です.また、コンパイラがプログラマがカスタマイズした演算子であることを区別し、プログラマが内蔵タイプの演算子の性質を変更しないようにするには、1つのパラメータがオブジェクトである必要があります.次に,この方法により,複素数の加減算を実現する.
/*************************************************************************
    > File Name:      .cpp
    > Author: Tanswer_
    > Mail: [email protected]
    > Created Time: 2016 10 15      20 58 34 
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class Complex
{
private:
    double real;    //  
    double imag;    //  
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}


    friend Complex operator*(const Complex & A,const Complex & B);
    friend Complex operator/(const Complex & A,const Complex & B);
    friend Complex operator+(const Complex & A,const Complex & B);
    friend Complex operator-(const Complex & A,const Complex & B);

    void display() const;
};

/* +      */
Complex operator+(const Complex & A,const Complex & B) 
{
    Complex C;
    C.real = A.real + B.real;
    C.imag = A.imag + B.imag;
    return C;
};

/* -      */
Complex operator-(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real - B.real;
    C.imag = A.imag - B.imag;
    return C;
};

/* *      */
Complex operator*(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real * B.real - A.imag * B.imag;
    C.imag = A.imag * B.real + A.real * B.imag;
    return C;
};

/* /      */
Complex operator/(const Complex & A,const Complex & B)
{
    Complex C;
    double square = A.real*A.real + A.imag*A.imag;
    C.real = (A.real * B.real + A.imag * B.imag)/square;
    C.imag = (A.imag * B.real - A.real * B.imag)/square;
    return C;
};

void Complex::display() const 
{
    cout << real << "+" << imag << "i" << endl;
};

int main()
{
    Complex a(1.1,2.2);
    Complex b(3.3,4.4);

    Complex c;

    c = a + b; 
    cout << "a+b= ";
    c.display();


    c = a - b;
    cout << "a-b= ";
    c.display();

    c = a*b;
    cout << "a*b= ";
    c.display();

    c = a/b;
    cout << "a/b= ";
    c.display();

    return 0;
}

出力結果:
a+b= 4.4 + 6.6i
a-b= -2.2 + -2.2i
a*b= -6.05 + 12.1i
a/b= 2.2 + 0.4i

2点説明:1.クラスComplexのプライベートオブジェクトを使用するため、これらのグローバル関数をクラスの友元関数として宣言します.2.ポインタオペレータ(->)、下付きオペレータ([])、関数呼び出しオペレータ(())、代入オペレータ(=)はメンバー関数としてのみ再ロードできます.
リロード<>(入出力)C++システムは「<>」をリロードしましたが、入出力に組み込まれたデータ型のみ使用できます.新しいデータ型を独自に定義し、入出力で処理する必要がある場合は、再ロードします.
次に、入力演算子をグローバル関数として再ロードし、2つのdoubleタイプのデータを読み込み、1つの複素に変換して複素オブジェクトに保存します.
istream & operator>>(istream &in, Complex &A)
{
    in >> A.real >> A.imag;
    return in;
};

istreamは入力ストリームであり、cinはistreamクラスのオブジェクトである.クラスの友元関数として宣言しましたfriend istream & operator>>(istream &in,Complex &A);入力演算子を再ロードする場合は、参照によってパラメータが渡されます.入力したパラメータにはistreamクラスの参照が含まれており、戻り値は参照です.このような利点の1つは、次のようにチェーン入力(すなわち連続入力)を採用できることです.
complex a, b, c;
cin>> a >> b >> c;

同様に、入力と同様に、出力演算子を再ロードし、複素数を出力できるようにします.
ostream & operator<out,Complex &A)
{
    out << A.real << " + " << A.imag << "i";
    return out;
};

これにより,クラスのオブジェクトへの直接入出力操作を実現できる.
#include 
using namespace std;

class Complex
{
private:
    double real;    //  
    double imag;    //  
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}

    friend Complex operator +(const Complex & A,const Complex &B);
    /*            */
    friend istream & operator>>(istream &in,Complex &A);
    friend ostream & operator<out,Complex &A);

    friend Complex operator+(const Complex & A,const Complex & B);
    friend Complex operator-(const Complex & A,const Complex & B);
};

/* +      */
Complex operator+(const Complex & A,const Complex & B) 
{
    Complex C;
    C.real = A.real + B.real;
    C.imag = A.imag + B.imag;
    return C;
};

/* -      */
Complex operator-(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real - B.real;
    C.imag = A.imag - B.imag;
    return C;
};

istream & operator>>(istream &in, Complex &A)
{
    in >> A.real >> A.imag;
    return in;
};

ostream & operator<out,Complex &A)
{
    out << A.real << " + " << A.imag << "i";
    return out;
};

int main()
{
    Complex a,b,c;
    cin >> a >> b;

    c = b + a;
    cout << "a+b= " << c << endl;

    c = a - b;
    cout << "a-b= " << c << endl;

    return 0;
}

入力
1.1 2.2 3.3 4.4
出力:
a+b= 4.4 + 6.6i
a-b= -2.2 + -2.2i

再ロード[](下付き演算子)
デフォルトでは、下付き演算子を使用して境界オーバーフローをチェックする機能はありません.この機能は、リロードによって実現できます.
/*************************************************************************
    > File Name:        .cpp
    > Author: Tanswer_
    > Mail: [email protected]
    > Created Time: 2016 10 16      14 41 56 
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class Array
{
private:
    int length;
    int *num;

public:
    Array():length(0),num(NULL) {}
    Array(int n);

    int getLength() const { return length; }
    int & operator[](int);
    const int & operator[](int) const;

};

Array::Array(int n)
{
    num = new int[n];
    length = n;
};

/*        */
int & Array::operator[](int i)
{
    if(i >= length || i < 0)
        throw string("out of bounds!");
    return num[i];
};

/*      ,        ,    */
const int & Array::operator[](int i) const
{
    if(i >= length || i < 0)
        throw string("out of bounds!");
    return num[i];
};

int main()
{
    Array a(5);
    int i;
    try{
        for(i=0; ifor(i=0; i<6; i++)
            cout << a[i] << endl;
    }catch(string s){
        cerr << s << ",i= " << i << endl;
    }
    return 0;
}

出力結果:
0
1
2
3
4
out of bounds!,i= 5

stringやC++の異常処理を用いたが,疑問なのはスキップでき,配列が境界を越えて異常が発生したことに重点を置いた.a[5]a.operator[](5)に変換されます
リロード=(代入演算子)
オブジェクト間では相互に値を割り当てることもできます.オブジェクト間の値は、オブジェクト全体のメモリをビット単位でコピーするのではなく、メンバー変数を順次コピーします.一般的には、デフォルトの"="は私たちのニーズを満たすことができますが、クラスにポインタタイプのメンバー変数が含まれている場合、問題が発生する可能性があります.
次の例を見てみましょう.
/*************************************************************************
    > File Name:        .cpp
    > Author: Tanswer_
    > Mail: [email protected]
    > Created Time: 2016 10 20      13 19 03 
 ************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class Book
{
public:
    Book():price(0.0),bookmark(NULL),num(0) {}
    Book(double, int *,int);
    void setbookmark(int,int);  //    
    void display(); 

private:
    double price;   //    
    int *bookmark;  //  
    int num;        //     
};

Book::Book(double price,int *bm,int num):price(price),num(num)
{
    //        ,
    //                      
    int *bmtemp = new int[num];
    for(int i=0; ithis -> bookmark = bmtemp;
}

void Book::setbookmark(int page,int index)
{
    if(index >= num-1)
        cout << "out of bound!" << endl;
    else
        bookmark[index] = page;
}

void Book::display()
{
    cout << "price:" << price <cout << "bookmarks: ";
    for(int i=0; iif(i == num-1)
            cout << bookmark[i] << endl;
        else
            cout << bookmark[i] << ",";
    }
}

int main()
{
    int bookmark[] = { 1,49,56,290 };
    Book java,cpp(68.5,bookmark,4);
    cpp.display();

    java = cpp; //      
    java.setbookmark(100,2);
    cpp.display();

    return 0;
}

実行結果:
price:68.5
bookmarks: 1,49,56,290
price:68.5
bookmarks: 1,49,100,290

2回のdisplay()呼び出しの結果は、javaオブジェクトを呼び出すsetBookmark()関数がcppオブジェクトに影響を及ぼすことを示しています.上記のプログラムでは、付与文を実行するとcpp.bookmarkの値はjavaに割り当てられます.bookmarkでは、異なるオブジェクトのメンバー変数が同じ配列を指すため、相互に影響します.
この問題を解決するには、付与演算子を再ロードする必要があります.

Book & Book::operator=(const Book & b)
{
    if(this != &b)
    {
        this -> price = b.price;
        this -> num = b.num;

        // bookmark  
        int *bmtemp = new int[num];
        for(int i=0; ithis -> bookmark = bmtemp;
    }
    return *this;
}

リロード関数をBookクラスに入れて代入文を実行するとjava.operator=(cpp)に変換されます.関数体では、thisはjavaオブジェクトを指します.これによりjavaオブジェクトも独自の配列を持ち、2つのオブジェクトの間には影響がありません.