1、什么是运算符重载?
(1)运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
因为在实际中确实有这种需求,例如将两个类对象直接相加,直接比较两个类对象的大小.....等等,因为普通的运算符
不能完成这种功能,所以在适当的时候对运算符进行重载,能够给我们的编程带来很大的方便。
(2)运算符函数定义的一般格式:
<返回类型说明符> operator<运算符符号>(<参数表>)
{
<函数体>
}
2、运算符重载的分类:一元运算符重载、二元运算符重载
一元运算符表示的是这中运算符只需要一个数参与运算,而二元运算符表示这种运算符需要两个数参与运算;例如:两个是相加的
"+"运算,就是一个二元运算符,两个数相乘的 "*" 运算符也是一个二元运算符;而 "++" "--",还有作为取反的负号运算符 "-"
等等都是一元运算符。
(1)一元运算符重载,本例子中使用 "-" 和 "++"运算符来演示一元运算符重载的使用,"++"运算符的两种重载方式:前置++、后置++
按照运算符重载的方式来说,一般分为友元函数重载和成员函数重载两种方式,下面分别用代码进行说明:
1 #include <iostream> 2 using namespace std; 3 4 class Coordinate{ // 定义一个坐标类 5 public: 6 Coordinate(int x, int y) : m_iX(x), m_iY(y) { } // 构造函数 7 void print(void) { cout << m_iX << ", " << m_iY << endl; } // 成员函数 8 Coordinate& operator-(void); // "-" 运算符重载 9 Coordinate& operator++(void); // 前置"++" 运算符重载 10 Coordinate operator++(int); // 后置"++" 运算符重载 注意返回值类型和上面的不一样,后面再说 11 private: 12 int m_iX; 13 int m_iY; 14 }; 15 16 Coordinate& Coordinate::operator-(void) // "-" 运算符重载函数定义 17 { 18 this->m_iX = -(this->m_iX); 19 this->m_iY = -(this->m_iY); 20 return *this; 21 } 22 23 Coordinate& Coordinate::operator++(void) // 前置"++" 运算符重载函数定义 24 { 25 ++(this->m_iX); 26 ++(this->m_iY); 27 return *this; 28 } 29 30 Coordinate Coordinate::operator++(int) // 后置"++" 运算符重载函数定义 31 { 32 Coordinate old(*this); 33 (this->m_iX)++; 34 (this->m_iY)++; 35 return old; 36 } 37 38 int main(void) 39 { 40 Coordinate coor(2,5); // 实例化一个Coordinate类对象 41 coor.print(); 42 ++coor; // 进行前置 ++ 运算 43 coor.print(); 44 coor++; // 进行后置 ++ 运算 45 coor.print(); 46 -coor; // 进行取反运算 47 coor.print(); 48 return 0; 49 } 一元运算符的成员函数重载
注意到代码中 "++" 运算的前置和后置的申明时区别在于,前置"++"的参数是void,而后置"++"的参数中加上了一个int,注意这个int是必须的(而且只能是int),它是用来说明这 个"++"是作为后置"++"来处理的,所以只要把它理解为一个标记即可。
Coordinate& operator++(void); // 前置"++" 运算符重载函数声明
Coordinate& operator++(int); // 后置"++" 运算符重载函数声明
1 #include <iostream> 2 using namespace std; 3 4 class Coordinate{ // 定义一个坐标类 5 friend Coordinate& operator-(Coordinate &c); // "-" 运算符重载 我们这里的参数使用的是引用,下面会说为什么要用引用 6 friend Coordinate& operator++(Coordinate &c); // 前置"++" 运算符重载 7 friend Coordinate operator++(Coordinate &c, int); // 后置"++" 运算符重载,同样使用int作为标记,这里的返回值和成员函数重载的返回值是一样的,下面会说 8 public: 9 Coordinate(int x, int y) : m_iX(x), m_iY(y) { } // 构造函数 10 void print(void) { cout << m_iX << ", " << m_iY << endl; } // 成员函数 11 private: 12 int m_iX; 13 int m_iY; 14 }; 15 16 Coordinate& operator-(Coordinate &c) // "-" 运算符重载函数定义 17 { 18 c.m_iX = -(c.m_iX); 19 c.m_iY = -(c.m_iY); 20 return c; 21 } 22 23 Coordinate& operator++(Coordinate &c) // 前置"++" 运算符重载函数定义 24 { 25 ++(c.m_iX); 26 ++(c.m_iX); 27 return c; 28 } 29 30 Coordinate operator++(Coordinate &c, int) // 后置"++" 运算符重载函数定义 31 { 32 Coordinate old(c); 33 (c.m_iX)++; 34 (c.m_iY)++; 35 return old; 36 } 37 38 int main(void) 39 { 40 Coordinate coor(2,5); // 实例化一个Coordinate类对象 41 coor.print(); 42 ++coor; // 进行前置 ++ 运算 43 coor.print(); 44 coor++; // 进行后置 ++ 运算 45 coor.print(); 46 -coor; // 进行取反运算 47 coor.print(); 48 return 0; 49 } 一元运算符的友元函数重载
注意:成员函数重载和友元函数重载参数类型和返回值类型的区别,不是非得这样做,只是为了符合运算符功能的要求。
(2)二元运算符重载:以 "+"、"<<"、"[]" 运算符为例子进行说明
1 #include <iostream> 2 using namespace std; 3 4 class Coordinate{ // 定义一个坐标类 5 friend ostream &operator<<(ostream &cout, const Coordinate &c); // 输出运算符 << 重载函数 只能使用友元函数进行重载 后面会说 6 public: 7 Coordinate(int x, int y) : m_iX(x), m_iY(y) { } // 构造函数 8 void print(void) { cout << m_iX << ", " << m_iY << endl; } // 成员函数 9 Coordinate operator+(const Coordinate &c); // + 号运算符重载 10 int operator[](int i); // [] 号运算符重载,只能用成员函数进行重载 后面会说 11 private: 12 int m_iX; 13 int m_iY; 14 }; 15 16 ostream &operator<<(ostream &cou, const Coordinate &c) // 输出运算符 << 重载函数定义 17 { 18 cou << c.m_iX << "," << c.m_iY; 19 return cout; 20 } 21 22 Coordinate Coordinate::operator+(const Coordinate &c) // + 号运算符重载函数 23 { 24 Coordinate coor(0,0); 25 coor.m_iX = this->m_iX + c.m_iX; 26 coor.m_iY = this->m_iY + c.m_iY; 27 return coor; 28 } 29 30 int Coordinate::operator[](int i) // 索引运算符 [] 重载函数 31 { 32 if (0 == i) 33 return this->m_iX; 34 if (1 == i) 35 return this->m_iY; 36 37 // 如果传入的参数不是这些,我们应该在下面抛出一个异常,但是我这里没有做,因为我的重点不在这里,在实际的编程中应该加上 38 } 39 40 int main(void) 41 { 42 Coordinate coor1(2,5); // 实例化一个Coordinate类对象 43 Coordinate coor2(5,2); 44 Coordinate coor3 = coor1 + coor2; 45 46 cout << coor3 << endl; 47 cout << coor3[0] << "," << coor3[1] << endl; 48 49 return 0; 50 } 二元运算符重载
注意:输出运算符 "<<" 只能通过友元函数进行重载,不能使用成员函数进行重载;索引运算符 "[]" 只能通过成员函数进行重载,不能使用友元函数进行重载。
3、注意事项
(1)返回值类型的问题
返回值是什么类型运算符重载函数本身并没有限定,也就是说返回值的问题并不会影响编译的问题;因为我们进行运算符重载使之变为方便的同时一定不要对原有
运算符进行功能性的改变,也就是强调对运算符进行重载一定要有意义,否则没必要。不同的运算符具有不同的功能,所以为了配合这种功能,我们的重载函数的
返回值也应该有所变化: Coordinate &operator++() Coordinate operator++(int)
一个返回的是引用类型,一个返回的是类类型,为什么这样做,其实和 前置++ 和 后置++ 本身的运算功能有关。
(2)运算符重载的本质。
运算符重载本质就是函数重载,对于运算符的成员函数重载,因为成员函数会默认带上一个this指针,而友元函数没有this指针
#include <iostream> using namespace std; class Coordinate{ // 定义一个坐标类 friend Coordinate operator+(const Coordinate &c1, const Coordinate &c2); .......... public: Coordinate operator+(const Coordinate &c); // + 号运算符重载 .......... private: int m_iX; int m_iY; }; ...................... // 省略了重载函数定义 int main(void) { Coordinate coor1(1,2); Coordinate coor2(3,4); Coordinate coor3 = coor1 + coor2; //对于成员函数其实就是调用了 operator+(coor2),而对于友元函数即时调用了 operator+(coor1, coor2) return 0; }
注意:成员函数中的默认this指针总是作为第一个参数的,而这个参数的位置又直接和运算时的操作数的位置又是先联系在一起的;这是规定的,所以这也就是导致了有些运算符的重载 函数只能是友元函数,而有些运算符的重载函数只能是成员函数。
(3)对于运算符的友元重载函数来,他的参数中至少有一个类类型参数(类类型和引用)
例如对于我们的运算符的友元重载函数: friend Coordinate operator+(const Coordinate *c1, const Coordinate *c2); // 这样是错误的,因为没有一个参数是类类型参 // 数
friend Coordinate operator+(const Coordinate &c1, const Coordinate *c2); // 这样是正确的,因为至少有一个参数是类类型
friend Coordinate operator++(Coordinate &c); // 这样是正确的
friend Coordinate operator++(Coordinate *c); // 这样是错误的
(3)只能重载允许重载的运算符,不能创造新的运算符
(4)重载之后的功能应该与原来的运算符在功能上一致,只是适应了更多的数据类型。
(3)对于有些运算符不能同时定义友元重载函数和成员重载函数,而有些运算符可以同时定义友元函数和成员函数。
对于这个问题,网上有人说是C++本身规定的,=、[]、()和->四种运算符只能被重载为类的非静态成员函数。
1 #include <iostream> 2 using namespace std; 3 4 class Coordinate{ 5 friend Coordinate operator+(const Coordinate &c1, const Coordinate &c2); // "+" 运算符的友元函数重载 6 friend Coordinate &operator++(Coordinate &c); // 前置"++" 运算符的友元函数重载 7 public: 8 Coordinate(int x, int y) : m_iX(x), m_iY(y) { } 9 void print(void) { cout << m_iX << "," << m_iY << endl; } 10 Coordinate operator+(const Coordinate &c); // "+" 运算符的成员函数重载 11 Coordinate &operator++(void); // 前置"++" 运算符的成员函数重载 12 private: 13 int m_iX; 14 int m_iY; 15 }; 16 17 Coordinate operator+(const Coordinate &c1, const Coordinate &c2) 18 { 19 Coordinate temp(0,0); 20 temp.m_iX = c1.m_iX + c2.m_iX; 21 temp.m_iY = c1.m_iY + c2.m_iY; 22 return temp; 23 } 24 25 Coordinate Coordinate::operator+(const Coordinate &c) 26 { 27 Coordinate temp(0, 0); 28 temp.m_iX = this->m_iX + c.m_iX; 29 temp.m_iY = this->m_iY + c.m_iY; 30 return temp; 31 } 32 33 Coordinate &operator++(Coordinate &c) 34 { 35 ++c.m_iX; 36 ++c.m_iY; 37 return c; 38 } 39 40 Coordinate &Coordinate::operator++(void) 41 { 42 ++(this->m_iX); 43 ++(this->m_iY); 44 return *this; 45 } 46 47 int main(void) 48 { 49 Coordinate coor1(1, 2); 50 Coordinate coor2(3, 4); 51 Coordinate coor3 = coor1 + coor2; // 在友元重载函数和成员重载函数同时存在的情况下执行 "+" 运算没有问题 52 // ++coor3; // 在友元重载函数和成员重载函数同时存在的情况下执行 前置"++" 运算,编译不能通过 53 coor3.print(); 54 return 0; 55 } Code
从上面的代码中看出,当同时存在 “+” 运算符的友元函数重载和成员函数重载时,程序编译没有问题,而且优先执行的是成员函数重载方式的运算符;而对于 前置"++" 运算符,同时存 在友元函数重载和成员函数重载时,执行 ++coor3; 却导致编译报错,显示有多个运算符与操作数匹配,操作不明确。 对于这个我实在是有点理解不了。
(4)主要是用于面向对象之间的重载。
(5)当需要重载运算符可以使用友元函数和成员函数时,应选择重载为成员函数。
因为对于友元函数来说,之前说过,在能够不需要使用的情况下,尽量不要使用。
(6)重载函数的参数不能超过2个。