C++中的运算符重载
2019-04-02 本文已影响8人
CurryCoder
1.Cpp中的重载运算符和重载函数
- C++允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。当调用一个重载函数或重载运算符时,编译器通过调用使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
2.函数重载
-
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。函数重载的实例如下,其中print()函数就是重载函数:
#include "iostream" #include "cstring" using namespace std; class A{ public: void print(int i){ cout << "整数为: " << i << endl; } void print(double d){ cout << "浮点数为: " << d << endl; } void print(char c[]){ cout << "字符串为: " << c << endl; } }; int main(){ A a; // 输出整数 a.print(2); // 输出浮点数 a.print(3.14); // 输出字符串 char c[] = "Hello Curry"; a.print(c); cout << "----------------------------------------------\n"; return 0; }
- 构造函数可以根据参数的不同实现函数重载,而析构函数没有参数,对于一个类来说也是唯一的,所以是不能重载
3.运算符重载
- 可以重定义或重载大部分C++内置的运算符。这样,就能使用自定义类型的运算符。重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后面要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。例如:
Box operator+(const Box&);
- 声明加法运算符用于把两个Box对象相加,返回最终的Box对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
- 运算符重载的实例,对象作为参数进行传递,对象的属性使用 this 运算符进行访问,如下所示:
// 运算符重载 class Box{ public: double getVolume(){ return length * width * height; } void setLength(int len){ length = len; } void setWidth(int wid){ width = wid; } void setHeight(int hei){ height = hei; } // 运算符重载:用于把两个box对象相加 Box operator+(const Box& b){ Box box; box.length = this->length + b.length; box.width = this->width + b.width; box.height = this->height + b.height; return box; } private: int length; int width; int height; }; int main(){ Box box1; Box box2; Box box3; double volume = 0.0; box1.setLength(6.0); box1.setWidth(7.0); box1.setHeight(5.0); box2.setLength(12.0); box2.setWidth(13.0); box2.setHeight(10.0); volume = box1.getVolume(); cout << "box1的体积是: " << volume << endl; volume = box2.getVolume(); cout << "box2的体积是: " << volume << endl; box3 = box1 + box2; volume = box3.getVolume(); cout << "box3的体积是: " << volume << endl; cout << "----------------------------------------------\n"; return 0; }
4.可重载运算符与不可重载运算符
-
可重载运算符列表如下:
可重载运算符.png -
不可重载运算符列表如下:
不可重载运算符.png
5.运算符重载实例
运算符重载实例.png5.1 一元运算符重载
- 一元运算符只对一个操作数进行操作,下面是一元运算符的实例:
- ++和--运算符重载
// ++与--运算符重载 class Time{ private: int hours; int minutes; public: Time(){ hours = 0; minutes = 0; } Time(int h, int m): hours(h), minutes(m){ cout << "Time类对象的构造函数\n"; } void display(){ cout << "H:" << hours << " M:" << minutes << endl; } // 重载前缀递增++运算符 Time operator++(){ ++minutes; // 对象加 1 if(minutes >= 60){ ++hours; minutes-= 60; } return Time(hours, minutes); } // 重载后缀递增++运算符 Time operator++(int){ Time T(hours, minutes); // 保存原始值 ++minutes; // 对象加 1 if(minutes >= 60){ ++hours; minutes-= 60; } return T; // 返回旧的原始值 } };
- ++和--运算符重载注意点:
- 递增和递减一般是改变对象的状态,所以一般是重载为成员函数
- 重载递增递减,一定要和指针的递增递减区分开。因为这里的重载操作的是对象,而不是指针(由于指针是内置类型,指针的递增递减是无法重载的),所以一般情况的递增递减是操作对象内部的成员变量。
- 递增和递减分为前置和后置情况,a = ++b;(前置), a = b++;(后置)。因为符号一样,所以给后置版本加一个int形参作为区分,这个形参是0,但是在函数体中是用不到的,只是为了区分前置后置。例如:
// ++与--运算符重载注意的点 class CA{ public: int pos; // 前置++就是增加当前对象的pos的值,并返回当前对象 CA operator++(){ pos++; return *this; } // 后置++就是增加当前对象的pos的值,并返回增加pos之前的对象 CA operator++(int){ CA temp = *this; ++*this; return temp; } }; CA ca,cb; ca.pos = 1; cb = ca++; cout << "cb = " << cb.pos << endl; cb = ++ca; cout << "cb = " << cb.pos << endl;
- ++和--运算符重载注意点:
- 一元减运算符,即负号(-)
// 重载一元减运算符- class Distance{ private: int feet; int inches; public: Distance(){ feet = 0; inches = 0; } Distance(int f, int i): feet(f), inches(i){ cout << "Distance类对象的构造函数\n"; } void displayDistance(){ cout << "F = " << feet << " I = " << inches << endl; } // 重载运算符- Distance operator-(){ feet = -feet; inches = -inches; return Distance(feet, inches); } }; Distance D1(10, 11), D2(-5, 11); -D1; D1.displayDistance(); -D2; D2.displayDistance();
- 逻辑非运算符(!)
- ++和--运算符重载
5.2 二元运算符重载
- 二元运算符需要两个参数,下面是二元运算符的实例。我们平常使用的加运算符( + )、减运算符( - )、乘运算符( * )和除运算符( / )都属于二元运算符。就像加(+)运算符。
class Box{ public: double getVolume(){ return length * width * height; } void setLength(int len){ length = len; } void setWidth(int wid){ width = wid; } void setHeight(int hei){ height = hei; } // 运算符重载:用于把两个box对象相加 Box operator+(const Box& b){ Box box; box.length = this->length + b.length; box.width = this->width + b.width; box.height = this->height + b.height; return box; } private: int length; int width; int height; }; int main(){ Box box1; Box box2; Box box3; double volume = 0.0; box1.setLength(6.0); box1.setWidth(7.0); box1.setHeight(5.0); box2.setLength(12.0); box2.setWidth(13.0); box2.setHeight(10.0); volume = box1.getVolume(); cout << "box1的体积是: " << volume << endl; volume = box2.getVolume(); cout << "box2的体积是: " << volume << endl; box3 = box1 + box2; volume = box3.getVolume(); cout << "box3的体积是: " << volume << endl; cout << "----------------------------------------------\n"; return 0; }
5.3 关系运算符重载
- C++ 语言支持各种关系运算符( < 、 > 、 <= 、 >= 、 == 等等),它们可用于比较 C++ 内置的数据类型,可以重载任何一个关系运算符,重载后的关系运算符可用于比较类的对象。
class Distance{ private: int feet; int inches; public: Distance(){ feet = 0; inches = 0; } Distance(int f, int i): feet(f), inches(i){ cout << "Distance类对象的构造函数\n"; } void displayDistance(){ cout << "F = " << feet << " I = " << inches << endl; } // 重载关系运算符< bool operator < (const Distance& d){ if(feet < d.feet) return true; if(feet == d.feet && inches < d.inches) return true; return false; } }; if (D1 < D2) cout << "D1 < D2\n"; else cout << "D1 > D2\n";
5.4 输入输出运算符重载
- C++ 能够使用流提取运算符>>和流插入运算符<<来输入和输出内置的数据类型。可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。
class Distance{ private: int feet; int inches; public: Distance(){ feet = 0; inches = 0; } Distance(int f, int i): feet(f), inches(i){ cout << "Distance类对象的构造函数\n"; } void displayDistance(){ cout << "F = " << feet << " I = " << inches << endl; } // 重载输入输出运算符 friend ostream &operator<<(ostream &output, const Distance& D){ output << "F = " << D.feet << " I = " << D.inches << endl; return output; } friend istream &operator>>(istream &input, Distance &D){ input >> D.feet >> D.inches; return input; } }; Distance D3(11, 10), D4(3, 2), D5; cout << "请输入目标值D5 = "; cin >> D5; cout << "D3 = " << D3 << endl; cout << "D4 = " << D4 << endl; cout << "D5 = " << D5 << endl;
5.5 赋值运算符重载
- 像其他运算符一样,您可以重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。
class Distance{ private: int feet; int inches; public: Distance(){ feet = 0; inches = 0; } Distance(int f, int i): feet(f), inches(i){ cout << "Distance类对象的构造函数\n"; } void displayDistance(){ cout << "F = " << feet << " I = " << inches << endl; } // 赋值运算符重载 void operator=(const Distance& D){ feet = D.feet; inches = D.inches; } }; D1 = D2; cout << "D1: " << D1 << endl; D1.displayDistance();
- 当用用户自定义类型变量向内置类型变量赋值时,可以使用自定义类型的隐式转换
class Int{ private: int n; public: Int(int i){ cout << "Int对象的构造函数\n"; }; operator int(){ // 这里就是隐式转换声明,应注意到它与运算符重载的不同之处 return n; } }; Int int_obj(3); int i = int_obj; // 隐式调用转换函数 cout << "i = " << i << endl; cout << "int_obj = " << int_obj << endl; // 由于未重载Int的<<操作符,将隐式调用转换函数
5.6 函数调用运算符重载()
- 函数调用运算符()可以被重载用于类的对象。当重载()时,不是创造了一种新的调用函数的方式。相反地,这是创建一个可以传递任意数目参数的运算符函数。
class Distance{ private: int feet; int inches; public: Distance(){ feet = 0; inches = 0; } Distance(int f, int i): feet(f), inches(i){ cout << "Distance类对象的构造函数\n"; } void displayDistance(){ cout << "F = " << feet << " I = " << inches << endl; } // 重载函数调用运算符 Distance operator()(int a, int b, int c){ Distance D; // 进行随机计算 D.feet = a + c + 10; D.inches = b + c + 100; return D; } }; Distance D6; D6 = D1(10, 10, 10); cout << "D6: " << D6 << endl;
5.7 下标运算符重载[]
- 下标操作符[]通常用于访问数组元素,重载该运算符用于增强操作C++数组的功能
// 下标运算符重载 const int SIZE = 10; class safearray{ private: int arr[SIZE]; public: safearray(){ register int i; for(i = 0; i < SIZE; i++) arr[i] = i; } // 下标运算符重载 int & operator[](int i){ if(i > SIZE){ cout << "索引超过最大值: "; return arr[0]; } return arr[i]; } }; safearray sa; cout << "sa[2] = " << sa[2] << endl; cout << "sa[5] = " << sa[5] << endl; cout << "sa[12] = " << sa[12] << endl;
5.8 类成员访问运算符->重载
- 类成员访问运算符->可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符->必须是一个成员函数。如果使用了->运算符,返回类型必须是指针或者是类的对象。运算符->通常与指针引用运算符*结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
// 类成员访问运算符-> class OO{ static int i,j; public: void f() const{ // 常函数 cout << i++ << endl; } void g() const{ cout << j++ << endl; } }; // 静态成员定义 int OO::i = 10; int OO::j = 12; // 为上面的类实现一个容器 class ObjContainer { vector<OO*> a; public: void add(OO* obj) { a.push_back(obj); // 调用向量的标准方法 } friend class SmartPointer; }; // 实现智能指针,用于访问类 Obj 的成员 class SmartPointer { ObjContainer oc; int index; public: SmartPointer(ObjContainer& objc) { oc = objc; index = 0; } // 返回值表示列表结束 bool operator++() // 前缀版本 { if(index >= oc.a.size() - 1) return false; if(oc.a[++index] == 0) return false; return true; } bool operator++(int) // 后缀版本 { return operator++(); } // 重载运算符 -> OO* operator->() const { if(!oc.a[index]) { cout << "Zero value"; return (OO*)0; } return oc.a[index]; } }; const int sz = 10; OO o[sz]; ObjContainer oc; for(int i = 0; i < sz; i++) { oc.add(&o[i]); } SmartPointer sp(oc); // 创建一个迭代器 do { sp->f(); // 智能指针调用 sp->g(); } while(sp++);
6.运算符重载注意的点
- a.运算重载符不可以改变语法结构
- b.运算重载符不可以改变操作数的个数
- c.运算重载符不可以改变优先级
- d.运算重载符不可以改变结合性
7.重载、覆盖、隐藏(重定义)的区别:
- 重载:指的是函数具有的不同的参数列表,而函数名相同的函数。重载要求参数列表必须不同,比如参数的类型不同、参数的个数不同、参数的顺序不同。如果仅仅是函数的返回值不同是没办法重载的,因为重载要求参数列表必须不同。(发生在同一个类里)
- 覆盖:存在类中,子类重写从基类继承过来的函数。被重写的函数不能是static的。必须是virtual的。但是函数名、返回值、参数列表都必须和基类相同。(发生在基类和子类)
- 隐藏:子类重新定义父类中有相同名称的非虚函数(参数列表可以不同)。(发生在基类和子类)