函数重载就是对一个已有的函数赋予新的含义,使之实现新的功能。因此一个函数名就可以用来代表不同功能的函数,也就是一名多用。运算符也可以重载,即运算符重载(operator overloading)。
一、非成员、非友元的重载运算符
在C++中,我们可以通过重载运算符来使得自定义类型(比如复数类)可以像内置类型一样使用运算符。
题目:定义一个复数类Complex,重载运算符“+”,使之能用于复数的加法运算。将运算符函数重载为非成员、非友元的普通函数。编写程序,求两个复数之和。
解析:此题中要定义一个非成员函数、非友元的重载运算符函数来实现两个复数之和,则在重载函数中无法调用Complex类中的私有数据成员,则需要在Complex类中定义set和get函数来实现复数相加。代码如下:
#include <iostream>
using namespace std;
class Complex{private:double real;double imag;public:// 无参构造函数Complex(){this->real = 0;this->imag = 0;}// 有参构造函数Complex(double real, double imag): real(real), imag(imag){}// 定义修改数据成员的函数void setReal(int real){this->real = real;}void setImag(int imag){this->imag = imag;}// 定义获取数据成员的函数int getReal(){return real;}int getImag(){return imag;}// 定义显示结果函数void display(){cout <<"(" <<real <<"," <<imag <<"i)" <<endl;}
};
// 定义重载运算符函数
Complex operator +(Complex& c1, Complex& c2){return Complex(c1.getReal() + c2.getReal(), c1.getImag() + c2.getImag());
}
int main(){// 实例Complex对象c1,c2,c3;Complex c1(10.0, 5.0), c2(20.0, 30.0), c3;// 通过重载运算符函数 计算C1 + C2得到C3c3 = c1 + c2;// 显示结果c1.display();c2.display();c3.display();return 0;
}
运行结果如下图:
二、实现+、-、*、/运算符重载
在C++中,要将运算符重载为类的成员函数,通常对于二元运算符(如+、-、*、/),需要有一个操作数是当前类的对象(作为隐式的this指针),而另一个操作数作为函数的参数。对于一元运算符(如取反-),则不需要参数。
题目:定义一个复数类Complex,重载运算符“+”、“-”、“*”、“/”,使之能用于复数加、减、乘、除。运算符重载函数作为Complex类的成员函数。编程序,分别求两个复数之和、差、积和商。
解析:此题中是将重载运算符作为Complex类的成员函数,它接受一个const Complex&类型的参数,并返回一个新的Complex对象,这是两个复数的运算结果。例如当你使用+运算符来加两个Complex对象时,实际上是在调用这个成员函数。代码如下:
#include <iostream>
using namespace std;
class Complex{private:double real;double imag;public:// 无参构造函数Complex(){this->real = 0;this->imag = 0;}// 有参构造函数Complex(double real, double imag): real(real), imag(imag){}// 定义重载运算符 + 成员函数Complex operator + (const Complex& c){return Complex(real + c.real, imag + c.imag);}// 定义重载运算符 - 成员函数Complex operator - (const Complex& c){return Complex(real - c.real, imag - c.imag);}// 定义重载运算符 * 成员函数Complex operator * (const Complex& c){double realPart = real * c.real - imag * c.imag;double imagPart = real * c.imag + imag * c.real;return Complex(realPart, imagPart);}// 定义重载运算符 / 成员函数Complex operator / (const Complex& c){double denominator = real * c.real + imag * c.imag;double realPart = real * c.real - imag * c.imag;double imagPart = real * c.imag + imag * c.real;return Complex(realPart/denominator, imagPart/denominator);}// 定义显示结果函数void display(const char *oper){cout <<oper <<": (" <<real <<"," <<imag <<"i)" <<endl;}
};
int main(){// 实例Complex对象c1,c2,c3,c4,c5,c6;Complex c1(10.0, 5.0), c2(20.0, 30.0), c3, c4, c5, c6;// 开始计算c3 = c1 + c2; // +c4 = c1 - c2; // -c5 = c1 * c2; // *c6 = c1 / c2; // ///显示结果c1.display("c1");c2.display("c2");cout <<endl <<"Result of operation:" <<endl;c3.display("+");c4.display("-");c5.display("*");c6.display("/");return 0;
}
运行结果如下图:
三、任意组合进行运算
题目:定义一个复数类Complex,重载运算符“+”,使之能用于复数的加法运算。参加运算的两个运算量可以都是类对象,也可以其中有一个是整数,顺序任意。例如:c1 + c2,i + c1,c1 + i均合法(设i为整数,c1,c2为复数)。编程序,分别求两个复数之和、整数和复数之和。
解析:c1+c2为对象+对象,使用“二”中的运算符符重载成员函数即可;i+c1为整形+对象,其中i非Complex对象,所以需要通过运算符重载友元函数来实现; c1+i为对象 + 整形,则需要在Complex类内中定义重载运算符+的成员函数来实现。所以此题中需要对运算符+重载三次,代码如下:
#include <iostream>
using namespace std;
class Complex{private:double real;double imag;public:// 定义有参构造函数,并添加默认值Complex(double real = 0.0, double imag = 0.0): real(real), imag(imag){}// 声明重载运算符 + 的友元函数(用于整形 + 对象)friend Complex operator + (double, const Complex&);// 定义重载运算符 + 成员函数(用于对象 + 对象)Complex operator + (const Complex& c) const{return Complex(real + c.real, imag + c.imag);}// 定义重载运算符 + 成员函数(用于 对象 + 整形)Complex operator +(int val) const{return Complex(real + val, imag);}// 定义显示结果函数void display(const char* p){cout <<p <<": (" <<real <<"," <<imag <<"i)" <<endl;}
};
// 定义重载运算符 + 友元函数
Complex operator + (double val, const Complex& c2){return Complex(val + c2.real, c2.imag);
}
int main(){// 实例Complex对象c1,c2,c3,c4,c5Complex c1(10.0, 5.0), c2(20.0, 30.0), c3, c4, c5;int i = 10;// 计算结果c3 = c1 + c2;c4 = i + c1;c5 = c1 + i;// 显示结果c1.display("c1");c2.display("c2");cout <<endl <<"Result of operation:" <<endl;// 显示运算结果c3.display("c3");c4.display("c4");c5.display("c5");return 0;
}
这里使用常引用(const reference)和常成员函数(const member function)是为了提供几个关键的好处。
常引用(如const Complex& c):
- 避免数据拷贝:通过传递引用而不是值,可以避免复制对象,这在处理大型对象时尤其重要,因为它可以提高性能。
- 保护数据不被修改:const修饰符确保传递给函数的对象不会被函数内的代码修改。这对于那些只需要读取数据而不修改数据的函数来说是非常重要的。
常成员函数:
在成员函数后添加const关键字意味着这个成员函数不会修改调用它的对象的任何成员变量(除非它们被声明为mutable)。这有助于确保对象的封装性,并且让调用者知道这个函数是安全的,不会意外地改变对象的状态。
总之,使用常引用和常成员函数是C++中常见的做法,它们有助于确保数据的安全性和一致性,同时提高代码的可读性和可维护性。
思考:在上篇文章中讲过不同数据类型间的转换,此题是否也可以运用到此方法。完成此项工作,则需要定义Complex类的友元函数来实现,使用之能处理对象+对象的运算,而i变量不管在运算符左侧,还是右侧,通过隐式转换为Complex(i);当然Complex类中必须要定义只带一个参数的转换构造函数。代码如下:
#include <iostream>
using namespace std;
class Complex{private:double real;double imag;public:Complex(){this->real = 0.0;this->imag = 0.0;}// 定义一个有参构造函数Complex(double real): real(real){}// 定义有参构造函数,并添加默认值Complex(double real, double imag): real(real), imag(imag){}// 声明重载运算符 + 的友元函数friend Complex operator + (const Complex&, const Complex&);// 定义显示结果函数void display(const char* p){cout <<p <<": (" <<real <<"," <<imag <<"i)" <<endl;}
};
// 定义重载运算符 + 的友元函数
Complex operator + (const Complex& c1, const Complex& c2){return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
int main(){// 实例Complex对象c1,c2,c3,c4,c5Complex c1(10.0, 5.0), c2(20.0, 30.0), c3, c4, c5;int i = 10;// 计算结果c3 = c1 + c2; // c1 + c2c4 = i + c1; // Complex(i) + c1,隐式转换c5 = c1 + i; // c1 + Complex(i), 隐式转换// 显示结果c1.display("c1");c2.display("c2");cout <<endl <<"Result of operation:" <<endl;// 显示运算结果c3.display("c3");c4.display("c4");c5.display("c5");return 0;
}
运算结果如下:
四、重载运算符>>和<<
题目:有两个矩阵a和b,均为2行3列。求两个矩阵之和。重载运算符“+”,使之能用于矩阵相加(c = a + b)。重载流插入运算符“<<”和流提取运算符“>>”,使之能用于该矩阵的输入和输出。
解析:定义一个Matrix类:
- 构造函数Matrix(int rows, int cols)初始化一个指定行数和列数的矩阵。
- 重载的operator+函数用于矩阵相加。
- 重载的operator<<函数用于输出矩阵的内容到输出流(通常是std::cout)。
- 重载的operator>>函数用于从输入流(通常是std::cin)读取矩阵的内容。
运算符<<和>>重置函数形式:
istream & operator >> (istream &, 自定义类 &);
ostream & operator << (ostream &, 自定义类 &);
代码如下:
#include <iostream>
using namespace std;
class Matrix{private:int rows;int cols;public:// 无参构造函数Matrix(){this->rows = 0;this->cols = 0;}// 有参构造函数Matrix(int r, int c): rows(r), cols(c){}// 定义重载运算符 + 成员函数Matrix operator +(const Matrix& m){return Matrix(rows + m.rows, cols + m.cols);}// 声明重载的operator<<函数用于输出矩阵的内容到输出流friend ostream& operator <<(ostream&, Matrix&);// 声明重载的operator>>函数用于从输入流friend istream& operator >>(istream&, Matrix&);
};
// 定义重载的operator<<函数用于输出矩阵的内容到输出流ostream& operator <<(ostream& os, Matrix& m){cout <<m.cols <<" columns and " <<m.rows <<" rows of the matrix" <<endl;for(int i = 0; i < m.rows; i++){for(int j = 0; j < m.cols; j++){os <<"*"; } os <<endl;}return os;}// 定义重载的operator>>函数用于从输入流istream& operator >>(istream& is, Matrix& m){cout <<"Enter the columns and rows of the matrix (e.g. 2*3):" <<endl;is >>m.rows >>m.cols;return is;}int main(){// 定义矩阵对象a,b,cMatrix a, b, c;// 输入矩阵a、b的行和列cin >>a;cin >>b;// 计算求和,得到c(通过重载运算符+)c = a + b;// 输出结果cout <<c;return 0;
}
运行结果如下图:
五、类型转换函数
题目:处理一个复数与一个double数相加的运算,结果存放在一个double型的变量d1中,输出d1的值,再以复数形式输出此值。定义Complex(复数)类,在成员函数中包含重载类型转换运算符。
解析:为了处理一个复数与一个double数相加,并将结果存放在一个double型的变量中,首先需要定义一个Complex类来表示复数。这个类应该包含两个数据成员来分别表示复数的实部和虚部。然后需要重载类型转换运算符,以便可以将Complex对象转换为double类型。最后将double结果放到Complex类中以复数形式输出。代码如下:
#include <iostream>
using namespace std;
class Complex{private:double real;double imag;public:// 无参构造函数Complex(){this->real = 0;this->imag = 0;}// 有参构造函数Complex(double real, double imag): real(real), imag(imag){}// 重载类型转换运算符operator double() const{return real;}// 定义显示结果函数void display(){cout <<"(" <<real <<"," <<imag <<"i)" <<endl;}
};
int main(){// 实例Complex对象c1,c2Complex c1(10.0, 5.0);double num, num2;// num = 30.5 + c1;num2 = c1 + 10.5;// 以复数形式输出Complex(num, 0.0).display();Complex(num2, 0.0).display();return 0;
}
运行结果如下:
六、类对象之间转换
题目:定义一个Teacher(教师)类和一个Student(学生)类,二者有一部分数据成员是相同的,例如num(号码)、name(姓名)、sex(性别)。编写程序,将一个Student对象(学生)转换为Teacher(教师)类,只将以上3个相同的数据成员移植过去。可以设想为:一位学生大学毕业了,留校担任教师,他原有的部分数据对现在的教师身份来说扔然是有用的,应当保留并成员其教师的一部分。
解析:实现这个功能,需要在Teacher类中定义一个构造函数,该构造函数接受一个Student对象作为参数,并从中提取num、name和sex数据成员的值来初始化Teacher对象。通常被称为“拷贝构造函数”,实际上它不是从同一类型的对象拷贝数据,而是从另一个类型的对象拷贝数据。
代码如下:
#include <iostream>
#include <string>
using namespace std;
// 定义学生类
class Student{private:int num;string name;int age;public:// 定义有参构造函数Student(int num, string name, int age): num(num), name(name), age(age){}// 定义外部获取私有数据成员int getNum() const { return num; }string getName() const{ return name; }int getAge() const{ return age; }
};
// 定义教师类
class Teacher{private:int num;string name;int age;public:// 定义复制构造函数Teacher(const Student& s): num(s.getNum()), name(s.getName()), age(s.getAge()){}// 定义显示结果函数void display(){cout <<"num:" <<num <<endl;cout <<"name:" <<name <<endl;cout <<"age:" <<age <<endl;}
};
int main(){Student stu(1001, "Tom", 15);// 将学生类数据拷贝给教师类Teacher t(stu);// 输出教师信息t.display();return 0;
}
运行结果如下: