C++演算子リロード——特殊演算子のリロード

9252 ワード

1、代入演算子の再ロード=
割り当て演算子は、クラスオブジェクト間の相互割り当てに使用されます.付与演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数および一般関数として再ロードできません.
ユーザー定義のクラスでは、代入演算子がリロードされていない場合、C++コンパイラはクラスにデフォルトの代入演算子メンバー関数を提供します.
デフォルトの割り当て演算子は、右側のオブジェクトの非静的メンバーが等号の左側のオブジェクトにコピーされるまでビット単位でコピーされます.
リロード付与演算子関数はpublicでなければなりません.そうしないと、ユーザーがリロード付与演算子関数を定義しているため、コンパイラはデフォルトを提供しません.
クラスに再ロードされた付与演算子関数は継承できません.
通常、コンパイラが提供するデフォルトのリロード付与演算子関数は、オブジェクト間の付与の問題を解決しますが、クラスにポインタデータメンバーが含まれている場合、ポインタサスペンションの問題を引き起こしやすくなります.
したがって,この場合は付与演算子の再ロードが必要である.
一例として、次のプログラムはデフォルトのリロード付与演算子関数を使用し、Linuxで実行するとdouble freeの問題が検出されますが、実際にはメモリ漏れの問題(valgrindツールで検出)、すなわちポインタサスペンションがあります.
なぜなら、オブジェクト間で値を付与すると、オブジェクトs 2のポインタメンバーpstrの値がオブジェクトs 1のポインタメンバーpstrの値に付与され、このときオブジェクトs 2で申請したスタックスペースが忘れられ、プログラム終了時に誰も解放されず、
同時にs 2オブジェクトが解析されると,s 1が申請したスタック空間が解放され,s 1オブジェクトが解析されると,2回の解放という問題が生じる. 
#include <cstring>
#include <iostream> using namespace std; class MyString { public: MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);} ~MyString(){delete []pstr;} void display(void){cout<<pstr<<endl;} private: char *pstr; int count; }; int main(void) { MyString s1("Hello");s1.display(); MyString s2("World");s2.display(); s2 = s1;s2.display();//glibc detected : double free or corruption return 0; }

/** valgrind
Invalid free() / delete / delete[] / realloc()

LEAK SUMMARY:  definitely lost: 6 bytes in 1 blocks
**/  

次に、カスタム付与演算子を再ロードして、メモリの漏洩と2回の解放の問題を解決します. 
#include <cstring>
#include <iostream>
using namespace std;

class MyString
{
public:
    MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);}
    ~MyString(){delete []pstr;}
    void display(void){cout<<count<<":"<<pstr<<endl;}
    //operator overload : =
    MyString& operator=(MyString& ref)
    {
        if (this != &ref)
        {
            delete []pstr;
            count = ref.count;
            pstr = new char[count+1];
            strcpy(pstr, ref.pstr);
        }
        return (*this);
    }

private:
    char *pstr;
    int count;
};
int main(void)
{
    MyString s1("Hello");s1.display();
    MyString s2("World");s2.display();

    s2 = s1;s2.display();//OK

    return 0;
}

 
2、下付き演算子の再ロード[]
下付き演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数と一般関数として再ロードできません.
[]は左と右の両方の値として使用できるため、下付き演算子関数を再ロードすると参照が返されます.
他の演算子のリロードとは異なり、[]リロードの機能は多様であり、ユーザーの定義に完全に依存しますが、通常は配列に関連しています. 
再ロードされた下付き演算子関数には1つのパラメータしかありません.すなわち、使用方法はAclss[para]のみであり、パラメータがないかAclss[para 1,para 2]マルチパラメータの形式ではありません.
例:下付き演算子を再ロードすることで、クラス内の配列を容易に取得および変更できます.
#include <iostream>
using namespace std;

class MyArrary
{
public:
    MyArrary(int a1,int a2,int a3, int a4){arr[0]=a1;arr[1]=a2;arr[2]=a3;arr[3]=a4;}

    //operator overload : []
    int& operator[](int num)//            
    {
        if ((num < 0) || (num > 4))
        {
            cout <<"error:";
            num =  0;
        }
        return arr[num];
    }

private:
    int arr[4];
};

int main(void)
{
    MyArrary ma(1,2,3,4);
    cout << ma[0]<<endl;   //  
    ma[0] = 5;             //  
    cout << ma[5]<<endl;
    return 0;
} 

3、括弧演算子()の再ロード
カッコ演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数と一般関数として再ロードできません.
()は左としても右としても使用できるため、カッコ演算子関数を再ロードすると参照が返されます.
カッコ演算子関数を再ロードするパラメータの数には制限はありません.パラメータもありません.
例:カッコ演算子を再ロードすることで、クラス内の配列を容易に取得および変更できます. 
#include <iostream>
using namespace std;

class DArrary
{
public:
    DArrary(int num){int i = 0; for (i = 0; i< 9;i++) arr[i/3][i%3] = num++;}
    void display(void){int i = 0;for (i = 0; i< 9;i++) cout<<arr[i/3][i%3]<<" ";cout<<endl;}

    //operator overload : () Multiple Parameters
    int& operator()(int a, int b)
    {
        return arr[a][b];
    }

    //operator overload : ()   Singal Parameter
    int& operator()(int a)
    {
        return arr[a/3][a%3];
    }
    //operator overload : ()  None Parameter
    int& operator()(void)
    {
        return arr[0][0];
    }
private:
    int arr[3][3];
};
int main(void)
{
    DArrary arr(1);arr.display();
    cout << arr(0,0) << endl;    //  
    cout << arr(2,2) << endl;    //  
    arr(0,0) = 11;               //  
    arr(2,2) = 22;               //  
    cout << arr(0,0) << endl;
    cout << arr(2,2) << endl;

    cout << arr(7) << endl;      //  
    arr(7) = 33;                 //  
    cout << arr(7) << endl;

    cout << arr() << endl;
    arr() = 111;
    arr.display();
    return 0;
}