函数重载就是对一个已有的函数赋予新的含义,使之实现新的功能。因此一个函数名就可以用来代表不同功能的函数,也就是一名多用。运算符也可以重载,即运算符重载(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得到C3
c3 = 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,c5
Complex 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,c5
Complex c1(10.0, 5.0), c2(20.0, 30.0), c3, c4, c5;
int i = 10;
// 计算结果
c3 = c1 + c2; // c1 + c2
c4 = 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,c
Matrix 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,c2
Complex 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;
}
运行结果如下: