1. 运算符重载的定义 运算符重载也是实现多态的一个重要手段。运算符重载实现的是编译时的多态,即静态多态性。 C++ 预定义的运算符只针对基本数据类型,而对于自定义数据类型,比如类,却没有类似的运算符。 运算符函数定义的一般形式: 返回类型说明符 operator 运算符符号 ( 参数列表 ) { 函数体 } 2. 运算符重载的特点 运算符重载使用关键字 operator 关键字对重载函数进行标识和定义。运算符有 3 种形式,即前缀、后缀、中缀。它们的 operator 表示形式如下表。 运算符重载时参数个数必须固定。重载函数的参数个数与标准运算符一致,即对于一元运算符(前缀和后缀形式),重载函数有且只有一个参数;对于二元运算符(中缀形式),重载函数有且只有两个参数。 运算符重载针对类对象操作,即重载函数的参数至少有一个属于 class 类型。 #includeiostream using namespace std; class Add { public: int operand; Add() { operand = 0; } Add(int value) { operand = value; } }; Add operator+(Add a, int b) { Add sum; sum.operand = a.operand + b; return sum; } int main() { Add a(5), b; b = a + 8; cout "the sum is: " b.operand endl; system("pause"); return 0; } 3. 运算符重载的形式 运算符的重载形式有两种:一种是重载为类的成员函数,另一种是重载为类的友元函数。对于每一种重载形式,由于运算符不同,可以分为双目运算符和单目运算符的实现。 3.1 运算符重载为类的成员函数 当运算符重载为类的成员函数,称为运算符成员函数。实际使用时,总是通过该类的对象访问重载的运算符。运算符成员函数在类内声明,在类外定义,一般形式为: 返回类型 operator 运算符 ( 参数列表 ) 在类外定义的一般形式为: 返回类型 类名:: operator 运算符 ( 参数表 ) { 函数体 } 3.1.1 双目运算符重载为成员函数 左操作数是访问该重载运算符的对象本身的数据,此时成员运算符函数只有一个参数。如: class X { X operator+(X ob); } 双目运算符重载为成员函数后,就可以在主函数或者其他类中调用。在 C++ 中,一般有显式和隐式两种调用方法。 (1)显式调用:对象名 .operator 运算符号(参数列表),如 aa.operator+(bb); (2)隐式调用:对象名 重载的运算符号 对象名,如 aa+bb ; #includeiostream using namespace std; class A { public: A(int x = 0):n(x) { } int getn() { return n; } A operator+(A a); private: int n; }; A A::operator+(A a) { A temp; temp.n = n + a.n; return temp; } int main() { A ob1(1), ob2(3), ob3, ob4; ob3 = ob1 + ob2; ob4 = ob1.operator+(ob2); cout "ob3.n= " ob3.getn() endl; cout "ob4.n= " ob4.getn() endl; system("pause"); return 0; } 3.1.2 单目运算符重载为成员函数 单目运算符重载为成员函数时,操作数是访问该重载运算符对象本身的数据,由 this 指针指出 , 此时成员运算符函数没有参数。如 Class X { X operator++(); } 与双目运算符的重载类似,单目运算符重载为成员函数后,在调用时也有显式和隐式两种。 (1) 显式调用:对象名 .operator 运算符号 () ,如 a.operator++(); (2) 隐式调用:重载的运算符号 对象名,如 ++a; #includeiostream using namespace std; class A { public: A(int x = 0) { n = x; } int getn() { return n; } A operator++(); private: int n; }; A A::operator++() { ++n; return *this; } int main() { A ob(1); ++ob; cout "ob.n= " ob.getn() endl; ob.operator++(); cout "ob.n=" ob.getn() endl; system("pause"); return 0; } 3.2 运算符重载为类的友元函数 将重载的运算符成员函数定义为类的友元函数,称为友元运算符函数。它不是类的成员,在类内声明原型,在类外定义函数本身。由于友元运算符不是类的成员函数,不属于任何一个类对象,所以没有 this 指针。因此重载双目运算符时要与 2 个参数,重载单目运算符时要一个参数。友元运算符函数在类内声明的一般形式为: friend 返回类型 operator 运算符 ( 参数表 ) 在类外定义的一般形式为: 返回类型 operator 运算符 ( 参数表 ) { 函数体 } 3.2.1 双目运算符重载为友元函数 双目运算符重载为友元函数时,由于没有 this 指针,所以两个操作数都要通过友元运算符函数的参数指出。与运算符重载为类的成员函数类似,双目运算符重载为友元函数后,调用也有显式和隐式两种方法。 (1) 显式调用: operator 运算符号(参数 1 ,参数 2 ),如 operator+(a,b); (2) 隐式调用,如 a+b; #includeiostream using namespace std; class A { public: A(int x = 0, int y = 0):a(x),b(y){} int geta() { return a; } int getb() { return b; } friend A operator+(A p, A q); private: int a, b; }; A operator+(A p, A q) { A temp; temp.a = p.a + q.a; temp.b = p.b + q.b; return temp; } int main() { A ob1(1, 2), ob2(3, 4), ob3, ob4; ob3 = ob1 + ob2; ob4 = operator+(ob1, ob2); cout "ob3.a= " ob3.geta() endl; cout "ob4.a= " ob4.geta() endl; system("pause"); return 0; } 因为友元运算符函数不属于某一个类,在类外定义友元函数不需要加类名和域运算符。双目运算符重载为友元函数和双目运算符重载为成员函数的根本区别在于其操作数的个数不同,前者需要指定 2 个参数,而后者定义时只需要一个参数。 3.2.2 单目运算符重载为友元函数 与单目运算符重载为成员函数不同,单目运算符重载为友元函数时,由于没有 this 指针,所以操作数要通过友元运算符函数的参数指出。调用也分为显式和隐式两种。 (1) 显式: operator 运算符号 ( 参数 ), 如 operator++(a); (2) 隐式:对象名 重载的运算符号 对象名 , 如 ++a; #includeiostream using namespace std; class A { public: A(int x=0):n(x){} int getn() { return n; } friend A operator++(A a); private: int n; }; A operator++(A a) { ++a.n; return a; } int main() { A ob(3); cout "ob.n=" ob.getn() endl; ++ob; cout "ob.n=" ob.getn() endl; operator++(ob); cout "ob.n=" ob.getn() endl; system("pause"); return 0; } 在将运算符重载为友元函数时,除运算符 = () [] - 不能用友元函数重载外,其余运算符都可以重载。此外,使用友元函数重载单目运算符“ ++ ”和“ -- ”时,由于要改变数自身的值,所以应采用引用参数传递操作数,否则会出现错误。 4. 运算符成员函数与友元运算符函数比较 ( 1 )对于双目运算符,运算符成员函数是类的成员,带有 this 指针,只需 1 个参数;而友元运算符函数不是类的成员,不带 this 指针,参数必须是 2 个。对于单目运算符,成员运算符函数不带参数,而友元运算符函数必须带 1 个参数; ( 2 )双目运算符可被重载为友元函数,也可以重载为成员函数,但在运算符的左操作数是一个标准数据类型而右操作数是对象的情况下,必须将它重载为友元函数,原因是标准数据类型的数据不能产生对重载运算符的调用; ( 3 )运算符成员函数与友元运算符函数都可以显式和隐式方式调用; 总的来说,将运算符重载为成员函数或是友元函数,要根据实际情况和使用习惯决定。一般而言,对于双目运算符重载为友元函数较好,若运算符的操作数特别是左操作数需要进行类型转换,必须重载为友元运算符函数。若一个运算符需要修改对象的状态,则选择运算符成员函数较好。