c++学習ノート(9.オペレータリロード)


このセクションの知識点:
1.c++標準ライブラリ:
a.c++標準ライブラリはc++言語の一部ではなく、c++標準ライブラリはc++言語で作成されたクラスライブラリと関数の集合である.
c++標準ライブラリで定義されたクラスとオブジェクトはstdネーミングスペースにあり、c++標準ライブラリのヘッダファイルは持たない.h接尾辞であり、c++標準ライブラリはcライブラリの対応c++ライブラリの
b.c++標準ライブラリは、など、多くの一般的なデータ構造を事前に定義しています.
c.c++標準ライブラリのcoutとcinの使用方法(コードは以下の通り).
#include <cstdio>
#include <iostream>

using namespace std;

int main()
{
	int a;
	int b;
	printf("put a :
"); cin >> a; printf("put b :
"); cin >> b; cout << "sum is : " << a+b << endl; return 0; }

2.グローバル関数のオペレータの再ロード:
a.c++ではoperatorキーワードで関数拡張オペレータ、すなわちオペレータ再ロードを利用できます.
Operatorの本質は、関数リロードによってオペレータのリロードを実現することであるため、オペレータリロードは関数リロードのルールに従う.
b.c++のクラスの友元の定義:
private宣言により、クラスのメンバーは外部からアクセスできませんが、friendキーワードで友元として権限を開放できます.友元アクセスメンバー変数は、依然としてオブジェクトによってアクセスされ、メンバー関数とは大きく異なり、通常のメンバー関数はメンバー変数に直接アクセスでき、オブジェクトは必要ありません.
コードの例は次のとおりです.
#include <iostream>

using namespace std;
class test
{
private:
	int a;
	int b;
public:
	test (int a, int b)
	{
		this->a = a;
		this->b = b;
	}
	int geta()
	{
		return a;
	}
	int getb()
	{
		return b;
	}
	/*              */ 
	friend test operator+ (test& a1, test& a2); //     
};

test operator+ (test& a1, test& a2) //          
{
	test c(0,0);
	c.a = a1.a + a2.a;
	c.b = a1.b + a2.b;
	return c;
}
int main()
{
	test n1(1,2);
	test n2(2,3);
	test n3(0,0);
	cout << "a is : " << n3.geta() <<endl;
	cout << "b is : " << n3.getb() <<endl;
	n3 = n1 + n2;
	cout << "a is : " << n3.geta() <<endl;
	cout << "b is : " << n3.getb() <<endl;
	return 0;
}

 

注意:上のコードのn 1+n 2はoperator+(n 1,n 2)に等価です.
3.クラスのメンバー関数のオペレータの再ロード:
a.メンバー関数でオペレータを再ロード:グローバル関数より1つ少ないパラメータ、すなわち左オペランドであり、このクラスのオブジェクトでもある.friendキーを使用する必要はありません.
コードは次のとおりです.
#include <iostream>
#include <cstdio> 
using namespace std;
class test
{
private:
	int a;
	int b;
public:
	test(int a, int b)
	{
		this->a = a;
		this->b = b;
	}
	test operator+ (test& n2);
	friend test operator+ (test& n1, test& n2);
	friend ostream& operator<<(ostream& out , test& n);
};
ostream& operator<<(ostream& out , test& n)
{
	out << "a is : " << n.a <<endl;
	out << "b is : " << n.b ;
	return out;
}
test test::operator+ (test& n2) //           
{
	test n3(0,0);
	n3.a = a + n2.a;
	n3.b = b + n2.b;
	return n3;
}
test operator+ (test& n1, test& n2) //           
{
	test n3(0,0);
	n3.a = n1.a + n2.a + 2;
	n3.b = n1.b + n2.b + 2;
	return n3;
}
int main()
{
	test n1(2,2);
	test n2(3,3);
	test n3(0,0);
	//n3 = n1 + n2;          //   n3 = n1 + n2                            
	n3 = n1.operator+(n2);   //              n3 = n1 + n2; 
	n3 = operator+ (n1, n2); //              n3 = n1 + n2; 
	cout << n3 <<endl;       //  <<                     operator<<(cout,n3); 
	operator<<(cout, n3)<<endl;
	cout.operator<< (55);    //   << ostream            
	return 0;
} 
注:メンバー関数のオペレータの再ロード、
op 1シンボルop 2は、
op1.operatorシンボル(op 2)だからop 1はこのクラスのオブジェクトでなければならない
グローバル関数のオペレータの再ロード、
op 1シンボルop 2は、
operatorシンボル(op 1,op 2)だからop 1とop 2はどんなタイプでもいいです
4.メンバー関数とグローバル関数のオペレータ再ロードの適用状況:
   a.
左オペランドがクラスのオブジェクトでない場合は、グローバル関数のオペレータのみで再ロードできます.
   b.
左オペランドのクラスを変更できない場合は、前に再ロードした<<記号のようにグローバル関数を使用して再ロードします.ostreamというクラスは変更できないので、グローバル関数のオペレータのみを使用して再ロードできます.
   c.
実際には、多数のメンバー関数のオペレータリロードは、グローバル関数のオペレータに置き換えられますが、=代入オペレータ、[]配列演算オペレータ、()関数呼び出しオペレータ、->ポインタオペレータは、メンバー関数のみでリロードできます.(この4つのオペレータはc++コンパイラの特殊な規定でメンバー関数のみを使用してオペレータの再ロードを行うことができます.なぜコンパイルに違反したのかは聞かないでください.)
   d.
c++コンパイラは、各クラスにデフォルトの付与オペレータを提供します.デフォルトの付与オペレータは、単純な値のコピーを行うだけです.クラスにポインタメンバー変数(特にnewから出てきたメモリを指すポインタ)がある場合は、付与オペレータを再ロードする必要があります.そうしないと、構造関数のdeleteの場合、同じアドレスdeleteに2回!
コードは次のとおりです.
#include <iostream>

using namespace std;

class test
{
private:
	int a;
	int b;
public:
	test(int a, int b)
	{
		this->a = a;
		this->b = b;
	}
/*	test(const test& n)
	{
		cout<<"hhhhhhh"<<endl;
	}*/
	test& operator= (test& n2);
	friend ostream& operator<<(ostream& out , test& n);
};
ostream& operator<<(ostream& out , test& n)
{
	out << "a is : " << n.a <<endl;
	out << "b is : " << n.b ;
	return out;
}
test& test::operator= (test& n2)
{
	a = n2.a + 10;
	b = n2.b + 10;
	return *this;
}
/********************************************************************** 
            ,                 ,
             test a = b           

             =            ,    
       , test a; a = b;               

          ,             ,      
       
**********************************************************************/ 
int main()
{
	test a(2,2);
	test b = a; //               
	cout << b;
	
//	test b = test(0,0);
//	b = a;      //         =           
	test c = test(9,9);
	b = c;
	cout << b;
	return 0;
} 

注意:第一に、c++コンパイラはクラスにコピーコンストラクタを提供し、割り当てオペレータのリロード関数を提供します.両方のデフォルト関数の機能は同じで、クラスのメンバー変数を簡単にコピーします.
第2に、しかし2つの関数の間は互いに独立していて、test b=aのようなそれぞれの応用シーンがあります.クラス定義が初期化されると、クラスのコピーコンストラクション関数が呼び出されます.また,関数パラメータがクラスのときvoid fun(test a),関数呼び出しがパラメータ伝達を行うときにコピーコンストラクション関数を呼び出し,実虚パラメータ間の伝達を実現する.b=aの2つのクラスが直接値を割り当てる場合、デフォルト値オペレータリロード関数が呼び出されます.
第三に、この2つのデフォルトで提供される関数は、ユーザーが自分で定義すると、元のc++コンパイラが提供したデフォルト関数が失効します.
5.++オペレータのリロード:
   a.
c++では、1つの占有パラメータで前置演算と後置演算を区別し、++演算子のリロードはグローバル関数でもメンバー関数でもあり、コードは以下の通りである.
#include <iostream>
using namespace std;
class test
{
private:
	int a;
	int b;
public:
	test(int a, int b)
	{
		this->a = a;
		this->b = b;
	}
	friend ostream& operator<<(ostream& out , test& n);
	friend test operator++ (test& a, int);
	friend test& operator++ (test& a);
};
ostream& operator<<(ostream& out , test& n)
{
	out << "a is : " << n.a <<endl;
	out << "b is : " << n.b ;
	return out;
}
test operator++ (test& a, int) //a++      
{
	test ret = a;
	a.a++;
	a.b++;
	return ret;
}
test& operator++ (test& a)  //++a      
{
	++a.a;
	++a.b;
	return a;	
}
int main()
{
	test a(0,0);
	test b(9,9);
	test c(8,8);
	b = a++;
	
	cout << b << endl;
	cout << a << endl;
	
	cout << "~~~~~" << endl;
	
	c = ++a;
	cout << c << endl;
	cout << a << endl;
	return 0;
} 

注意:operator++(test&a,int)はa++後置演算、operator++(test&a)は++a前置演算
6.&&|||オペレータのリロード:
   a.
通常の&&&と|内蔵で短絡ルールが実装されているため、&&と|オペレータをリロードしないでください.ただし、オペレータのリロードは関数のリロードで完了し、操作数は関数パラメータとして伝達され、c++の関数パラメータは評価され、短絡ルールは実現できません.
コードは次のとおりです.
#include <cstdlib>
#include <iostream>

using namespace std;

class Test
{
    int i;
public:
    Test(int i)
    {
        this->i = i;
    }
    
    Test operator+ (const Test& obj)
    {
        Test ret(0);
        
        cout<<"Test operator+ (const Test& obj)"<<endl;
        
        ret.i = i + obj.i;
        
        return ret;
    }
    
    bool operator&& (const Test& obj)
    {
        cout<<"bool operator&& (const Test& obj)"<<endl;
        
        return i && obj.i;
    }
};

int main(int argc, char *argv[])
{
    int a1 = 0;
    int a2 = 1;
    
    if( a1 && (a1 + a2) )
    {
        cout<<"Hello"<<endl;
    }
    
    Test t1 = 0;
    Test t2 = 1;
    
    if( t1 && (t1 + t2) ) //        t1 0  t1+t2          
    {
        cout<<"World"<<endl;
    }
    
    return 0;
}

注意:通常上のt 1+t 2は短絡されるべきで、実行されるべきではありませんが、t 1+t 2は実質的に関数のパラメータなので、実行され、短絡規則に反しています!!
7.本節のまとめ(いくつかのよく出る問題):
   a.
=割り当てオペレータ、[]配列オペレータ、()関数呼び出しオペレータ、->ポインタオペレータの4つのオペレータは、メンバー関数のみでリロードできます.
   b.
++オペレータはintパラメータで前置と後置のリロードを行います.
   c.
c++の中で&&&&|||オペレータをリロードしないでください