C++演算子リロード——特殊演算子のリロード
9252 ワード
1、代入演算子の再ロード=
割り当て演算子は、クラスオブジェクト間の相互割り当てに使用されます.付与演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数および一般関数として再ロードできません.
ユーザー定義のクラスでは、代入演算子がリロードされていない場合、C++コンパイラはクラスにデフォルトの代入演算子メンバー関数を提供します.
デフォルトの割り当て演算子は、右側のオブジェクトの非静的メンバーが等号の左側のオブジェクトにコピーされるまでビット単位でコピーされます.
リロード付与演算子関数はpublicでなければなりません.そうしないと、ユーザーがリロード付与演算子関数を定義しているため、コンパイラはデフォルトを提供しません.
クラスに再ロードされた付与演算子関数は継承できません.
通常、コンパイラが提供するデフォルトのリロード付与演算子関数は、オブジェクト間の付与の問題を解決しますが、クラスにポインタデータメンバーが含まれている場合、ポインタサスペンションの問題を引き起こしやすくなります.
したがって,この場合は付与演算子の再ロードが必要である.
一例として、次のプログラムはデフォルトのリロード付与演算子関数を使用し、Linuxで実行するとdouble freeの問題が検出されますが、実際にはメモリ漏れの問題(valgrindツールで検出)、すなわちポインタサスペンションがあります.
なぜなら、オブジェクト間で値を付与すると、オブジェクトs 2のポインタメンバーpstrの値がオブジェクトs 1のポインタメンバーpstrの値に付与され、このときオブジェクトs 2で申請したスタックスペースが忘れられ、プログラム終了時に誰も解放されず、
同時にs 2オブジェクトが解析されると,s 1が申請したスタック空間が解放され,s 1オブジェクトが解析されると,2回の解放という問題が生じる.
LEAK SUMMARY: definitely lost: 6 bytes in 1 blocks
次に、カスタム付与演算子を再ロードして、メモリの漏洩と2回の解放の問題を解決します.
2、下付き演算子の再ロード[]
下付き演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数と一般関数として再ロードできません.
[]は左と右の両方の値として使用できるため、下付き演算子関数を再ロードすると参照が返されます.
他の演算子のリロードとは異なり、[]リロードの機能は多様であり、ユーザーの定義に完全に依存しますが、通常は配列に関連しています.
再ロードされた下付き演算子関数には1つのパラメータしかありません.すなわち、使用方法はAclss[para]のみであり、パラメータがないかAclss[para 1,para 2]マルチパラメータの形式ではありません.
例:下付き演算子を再ロードすることで、クラス内の配列を容易に取得および変更できます.
3、括弧演算子()の再ロード
カッコ演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数と一般関数として再ロードできません.
()は左としても右としても使用できるため、カッコ演算子関数を再ロードすると参照が返されます.
カッコ演算子関数を再ロードするパラメータの数には制限はありません.パラメータもありません.
例:カッコ演算子を再ロードすることで、クラス内の配列を容易に取得および変更できます.
割り当て演算子は、クラスオブジェクト間の相互割り当てに使用されます.付与演算子は、クラスの非静的メンバー関数としてのみ再ロードでき、友元関数および一般関数として再ロードできません.
ユーザー定義のクラスでは、代入演算子がリロードされていない場合、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;
}