演算子のオーバーロード
いわゆるオーバーロードとは、新しい意味を与えることです。関数のオーバーロードとは、既存の関数に新しい意味を与えて、新しい機能を実現することです。したがって、1 つの関数名を使用して、異なる機能を持つ関数を表すことができます。つまり、「1 つの名前で複数の用途がある」ということになります。
オペレーターも過負荷になる可能性があります。実際、私たちは気づかないうちに演算子のオーバーロードをすでに使用しています。たとえば、誰もが加算演算子「+」を使用して、5+8、5.8 +3.67 などの整数、単精度数値、および倍精度数値を加算することに慣れています。実際、コンピュータは整数、単精度数値を加算できます。加算演算のプロセスは大きく異なりますが、C++ では演算子 "+" がオーバーロードされているため、int、float、および doUble 型の演算に適用できます。
C++ の事前定義演算子のオペランドは、基本データ型のみにすることができます。しかし実際には、同様の操作が多くのユーザー定義型 (クラスなど) にも必要です。現時点では、これらの演算子を C++ で再定義し、既存の演算子に新しい関数を与えて、特定の型で特定の演算を実行できるようにする必要があります。演算子のオーバーロードの本質は関数のオーバーロードです。これは C++ のスケーラビリティを提供し、C++ の最も魅力的な機能の 1 つです。
演算子のオーバーロードは、オーバーロードされた演算子が実行する操作を定義する演算子関数を作成することによって実現されます。演算子関数の定義は他の関数の定義と似ていますが、唯一の違いは、演算子関数の関数名がキーワード演算子とオーバーロードされる演算子記号で構成されることです。演算子関数定義の一般的な形式は次のとおりです:
演算子 ()
{ <函数体> }
演算子をオーバーロードする場合は、次の規則に従う必要があります:
(1 )一般的な関係演算子「.」、メンバー ポインター演算子「.*」、スコープ演算子「::」、sizeof 演算子、および三項演算子「?:」を除き、C++ のすべての演算子はオーバーロードできます。
(2) オーバーロードされる演算子は、C++言語の既存の演算子の範囲内でオーバーロードが可能な演算子に限定され、新たな演算子を作成することはできません。
(3) 演算子のオーバーロードは本質的に関数のオーバーロードであるため、コンパイラーによる演算子のオーバーロードの選択は、関数のオーバーロードの選択原則に従います。
(4) オーバーロードされた演算子は、演算子の優先順位や結合性を変更したり、オペランドの数や演算子の構文構造を変更したりすることはできません。
(5) 演算子のオーバーロードでは、内部型オブジェクトの演算子の意味を変更できません。ユーザー定義型のオブジェクト、またはユーザー定義型のオブジェクトと内部型のオブジェクトが混在する場合にのみ使用できます。
(6) 演算子のオーバーロードは、新しいタイプのデータの実際のニーズに基づいて元の演算子を適切に変換するものであり、目的のないオーバーロードされた演算子の使用を避けるために、オーバーロードされた関数は元の関数と同様である必要があります。
演算子関数のオーバーロードには、通常、クラスのメンバー関数としてのオーバーロードと、クラスの非メンバー関数としてのオーバーロードの 2 つの形式があります。非メンバー関数は通常、フレンドです。 (演算子は、非メンバー、非フレンド関数としてオーバーロードできます。ただし、そのような演算子関数がクラスのプライベート メンバーと保護されたメンバーにアクセスする場合、パブリック インターフェイスで提供されるデータの設定メソッドとデータの読み取りメソッドを使用する必要があります。これらの関数を呼び出すとパフォーマンスが低下します)
メンバー関数演算子
メンバー関数としての演算子のオーバーロードの一般的な形式は次のとおりです:
<函数类型> operator <运算符>(<参数表>) { <函数体> }
演算子はクラスとしてオーバーロードされます。 メンバー関数が使用される場合、関数のパラメーターの数は元のオペランドより 1 つ減ります (後置単項演算子を除く)。これは、メンバー関数が暗黙的にそのオブジェクトにアクセスするためです。このポインターを使用するクラス。シンボル関数の左端のオペランドとして機能します。したがって:
(1) 二項演算子がクラスのメンバー関数としてオーバーロードされる場合、関数は演算子の右側のオペランドである 1 つのパラメーターのみを明示的に示します。
(2) 接頭辞付き単項演算子がクラスのメンバー関数としてオーバーロードされる場合、パラメーターを明示的に記述する必要はありません。つまり、関数には仮パラメーターがありません。
(3) 後置単項演算子がクラスのメンバー関数としてオーバーロードされる場合、関数には整数の仮パラメータが必要です。
メンバー関数演算子を呼び出す形式は次のとおりです:
<对象名>.operator <运算符>(<参数>)
<对象名><运算符><参数>
と同等です。 たとえば: a+b は a.operator + (b) と同等です。一般に、演算子の慣用的な表現を使用します。
フレンド関数演算子
クラスのフレンド関数としてオーバーロードされる演算子の一般的な形式は次のとおりです:
friend <函数类型> operator <运算符>(<参数表>) { <函数体> }
演算子がクラスのフレンド関数としてオーバーロードされる場合、暗黙的な this ポインターが存在しないため、オペランドの数は変更されていません。すべてのオペランドは関数の仮パラメータを介して渡される必要があり、関数のパラメータは左から右のオペランドに対応します。
フレンド関数演算子を呼び出す形式は次のとおりです。
operator <运算符>(<参数1>,<参数2>)
<参数1><运算符><参数2>
と同等です。 例: a+b は、演算子 + (a, b) と同等です。
2 つのオーバーロード形式の比較
在多数情况下,将运算符重载为类的成员函数和类的友元函数都是可以的。但成员函数运算符与友元函数运算符也具有各自的一些特点:
(1) 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
(2) 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
(3) 类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。
(4) 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
(5) 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
(6) 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一 个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。
(7) 当需要重载运算符具有可交换性时,选择重载为友元函数。
1 为什么会用运算符重载机制(回忆函数重载)
用复数类举例 //Complex c3 =c1 + c2;
//原因 Complex是用户自定义类型,编译器根本不知道如何进行加减
//编译器给提供了一种机制,让用户自己去完成,自定义类型的加减操作。。。。。
//这个机制就是运算符重载机制
2 运算符重载的本质是一个函数
例如1:
//通过类成员函数完成 - 操作符重载(函数的定义在类里面) //函数声明 Complex operator-(Complex &c2) //用类成员函数实现-运算符重载 Complex c4 = c1 - c2; c4.printCom(); //打印输出 //c1.operator-(c2);
例如2:
//通过全局函数方法完成+操作符重载(函数的定义在类外) //函数声明 Complex operator+(Complex &c1, Complex &c2) //函数调用分析 int main() { Complex c1(1, 2), c2(3, 4);//定义两个对象 //Complex c31 = operator+(c1,c2); 函数的调用模式 Complex c3 = c1 + c2; //简写+ c3.printCom(); }
感受操作过程:
例如3:
//前置++操作符 用全局函数实现 Complex& operator++(Complex&c1) { c1.a ++; c1.b ++; return c1; } //调用方法 ++c1 ; //=è需要写出操作符重载函数原形 c1.printCom(); //运算符重载函数名定义 //首先承认操作符重载是一个函数 定义函数名--->operator++ //分析函数参数 根据左右操作数的个数------->operator++(Complex &c1) //分析函数返回值-------> Complex&operator++(Complex &c1) 返回它自身
例如4
//4.1 前置—操作符成员函数实现 Complex& operator--() { this->a--; this->b--; return *this; //注意返回 } //4.2 调用方法 --c1; c1.printCom(); //4.3前置—运算符重载函数名定义 //c1.operator--()
例如5:
//5.1//后置++ 操作符用全局函数实现 Complex operator++(Complex &c1, int) { Complex tmp = c1; c1.a++; c1.b++; return tmp; } //5.2 调用方法 c1 ++ ; //先使用后++ //5.3 后置++运算符重载函数名定义 Complex operator++(Complex&c1, int) //函数占位参数 和 前置++ 相区别
例如6:
//6.1 后置— 操作符 用类成员函数实现 Complexoperator--(int) { Complextmp = *this; this->a--; this->b--; return tmp; } //6.2 调用方法 c1 ++ ; //先使用后++ //6.3 后置--运算符重载函数名定义 Complex operator--(int) //函数占位参数和 前置-- 相区别
前置和后置运算符总结
C++中通过一个占位参数来区分前置运算和后置运算
定义运算符重载函数名的步骤
全局函数、类成员函数方法实现运算符重载步骤
1)要承认操作符重载是一个函数,写出函数名称operator+ ()
2)根据操作数,写出函数参数
3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
友元函数实现操作符重载的应用场景
1)友元函数和成员函数选择方法 Ø 当无法修改左操作数的类时,使用全局函数进行重载 Ø =, [], ()和->操作符只能通过成员函数进行重载 2)用友元函数重载 << >>操作符 Ø istream 和 ostream 是 C++ 的预定义流类 Ø cin 是 istream 的对象,cout 是 ostream 的对象 Ø 运算符 << 由ostream 重载为插入操作,用于输出基本类型数据 Ø 运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据 Ø 用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型 a)用全局函数方法实现 << 操作符 ostream& operator<<(ostream &out, Complex &c1) { //out<<"12345,生活真是苦"<<endl; out<<c1.a<<"+ "<<c1.b<<"i "<<endl; return out; } //调用方法 cout<<c1; //链式编程支持 cout<<c1<<"abcc"; //cout.operator<<(c1).operator<<("abcd"); //函数返回值充当左值 需要返回一个引用 b)类成员函数方法无法实现 <<操作符重载 //因拿到cout这个类的源码 //cout.operator<<(c1); 3) 友元函数重载操作符使用注意点 a) 友员函数重载运算符常用于运算符的左右操作数类型不同的情况 b)其他 Ø 在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择 Ø 友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换 Ø C++中不能用友员函数重载的运算符有 = () [] ->
请问:为什么运算法重载成员函数只有一个参数,而友元函数有两个?
由于成员函数有指向对象的this指针,可以通过对象传递参数;而友元函数没有this指针,所以必须要两个参数。(针对二元操作符重载而言)
以上就是c++复习要点总结之七——运算符重载的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!