C++面向对象编程之六:重载操作符(<<,>>,+,+=,==,!=,=)
创始人
2024-06-01 08:02:23
0

重载操作符

C++允许我们重新定义操作符(例如:+,-,*,/)等,使其对于我们自定义的类类型对象,也能像内置数据类型(例如:int,float,double)等一样直观,可以进行加,减,乘,除,比较大小等等操作。

重载操作符本质是函数,只是这个函数的函数名比较特别,为:operator后接需要重新定义的操作符的符号。例如,重载+号,函数名为:operator+;重载-号,函数名:operator-。因为重载操作符本质是函数,所以实际上就是为某个自定义的数据类类型或枚举类型实现函数重载,比如内置int类型已经有

int operator+(int, int)版本,我们有一个自定义的complex(复数类),要想跟内置的int类型有一样直观的操作两个复数相加,只要我们为自定义的complex(复数类)重定义操作符+,提供complex operator+(complex, complex)版本即可。

重载操作符语法

返回值类型 operator操作符号(形参列表)
{
}

重载操作符的两种形式

对于大多数重载操作符来说,可以定义为全局函数或类的成员函数。

1.重载操作符为全局函数,并且通常必须将这个全局函数设置为所操作类的友元

#include 
using namespace std;class MyInt
{friend MyInt operator+(const MyInt &a, const MyInt &b); //这个函数为MyInt类的友元public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}int getNum() const{return m_num;}private:int m_num;
};//重载操作符为全局函数
MyInt operator+(const MyInt &a, const MyInt &b)
{MyInt temp;temp.m_num = a.m_num + b.m_num; //如果没有把这个全局函数设置为操作类MyInt的友元,则不能直接访问MyInt类的//私有成员变量m_numreturn temp;
}int main(int argc, char *argv[])
{MyInt a(1);MyInt b(2);MyInt c = a + b; //本质是:调用了全局函数,即 MyInt c = operator+(a, b);MyInt d = operator+(a, b); cout << "a + b = " << c.getNum() << endl;cout << "operator+(a, b) = " << d.getNum() << endl;return 0;
}

注意:重载操作符为全局函数时,本质是调用了该全局函数。例如:MyInt c = a + b; //本质是:调用了全局函数,即 MyInt c = operator+(a, b);

2.重载操作符为类的成员函数

#include 
using namespace std;class MyInt
{public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}int getNum() const{return m_num;}MyInt operator+(const MyInt &b) //重载操作符为类成员函数{MyInt temp;temp.m_num = this->m_num + b.m_num;return temp;}private:int m_num;
};int main(int argc, char *argv[])
{MyInt a(1);MyInt b(2);MyInt c = a + b; //本质是:调用了MyInt类的成员函数,即 MyInt c = a.operator+(b);MyInt d = a.operator+(b);cout << "a + b = " << c.getNum() << endl;cout << "a.operator+(b) = " << d.getNum() << endl;return 0;
}

注意:重载操作符为类的成员函数时,本质是调用了该类的成员函数。例如:MyInt c = a + b; //本质是:调用了MyInt类的成员函数,即 MyInt c = a.operator+(b);

重载操作符为全局函数和重载操作符为类的成员函数的形参区别

对于重载操作符为全局函数和重载操作符为类的成员函数,形参的个数看上去是有区别的,一般重载操作符为类的成员函数,其形参个数比重载操作符为全局函数的形参个数少1个,因为重载操作符为类的成员函数,类中有一个隐含的this指针形参,限定为重载操作符函数的第一个形参。

重载操作符为全局函数还是重载操作符为类的成员函数,应该怎么选择?

对于重载操作符可以为一个普通的全局函数或一个类的成员函数这两种方式来实现,我的建议是,对于能够使用类的成员函数来实现的,我们应该选择用类的成员函数来实现,因为选择用普通的全局函数来实现,需要把这个全局函数设置为操作类的友元,从而达到可以直接访问该操作类的非共有成员变量的目的,但这破坏类的封装性。

不能重载的操作符

.         :类的对象访问操作符
.*  ->*   :类的对象或或类的对象指针访问类中的函数指针操作符
::        :域操作符
?=        :条件操作符
sizeof    :长度操作符
#         :预处理操作符         

重载操作符可以给我们自定义的类类型对象的进行操作符运算的时候更加直观。但不要滥用重载操作符。

1.不能改变内置类型的操作符的含义。例如:int operator+(int, int)

2.不能为内置类型定义额外的新的操作符。例如:不能定义两个数组为操作数的operator+

3.逗号(,),取地址(&),逻辑与(&&),逻辑或(||)这些操作符具有有用的内置含义,不建议重载。

假设我们有一个MyInt类,实现的功能跟内置的类型int一样。那么下面我们为这个MyInt类实现一下相关的操作符重载吧。

重载输出操作符<<

分析:

对于内置的类型int
int a = 0;
cout << a << endl;
<< 操作符有两个操作数,
第一个(左)操作数数据类型为:ostream&,
第二个(右)操作数数据类型为:const int&
所以我们只能重载输出操作符为全局函数,不能为MyInt类的成员函数,
因为重载操作符为类的成员函数的第一个操作数数据类型限定为this指针,
而我们需要的是第一个操作数数据类型为:ostream&,
因为输出a后,后面的操作是还能输出换行的,所以返回值类型为ostream&

经过以上分析,我们可以得出,重载输出操作符<<的全局函数为:

ostream& operator<<(ostream &cout, const MyInt a)

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}int main(int argc, char *argv[])
{MyInt a;cout << a << endl;return 0;
}

重载输入操作符>>

分析:

对于内置的类型int
int a = 0;
int b = 0;
cin >> a >> b;
>> 操作符有两个操作数,
第一个(左)操作数数据类型为:istream&,
第二个(右)操作数数据类型为:const int&
所以我们只能重载输出操作符为全局函数,不能为MyInt类的成员函数,
因为重载操作符为类的成员函数的第一个操作数数据类型限定为this指针,
而我们需要的是第一个操作数数据类型为:istream&,
因为输入a后,后面的操作是还能输入b的,所以返回值类型为istream&

经过以上分析,我们可以得出,重载输入操作符>>的全局函数为:

istream& operator>>(istream &cin, MyInt &a)

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);friend istream& operator>>(istream &cin, MyInt &a);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}istream& operator>>(istream &cin, MyInt &a)
{cin >> a.m_num;return cin;
}int main(int argc, char *argv[])
{MyInt a;MyInt b;cout << "请输入a和b的值,用空格隔开:" << endl;cin >> a >> b;cout << "a = " << a << ",b = " << b << endl;return 0;
}

算术运算符和关系运算符

一般来说,我们应该将算术操作符和关系操作符定义为非成员函数。这是因为,算术操作符和关系操作符中,例如:有左操作数为int a = 1,右操作数为double b = 2.12345,进行 a + b运算时,左操作数a会转成跟右操作数b的精度一样的double类型,即:a + b = 1.000000000000000(双精度保留15位小数)+ 2.123450000000000(双精度保留15位小数),如果将算术操作符和关系操作符定义定义为类的成员函数时,左操作数限定为隐含的this指针,就违背了在C/C++中,如果一个表达式中含有不同类型的常量或变量,在计算时,会将它们自动转换为精度更高的同一种数据类型。

重载相加操作符+

1.返回值类型为:MyInt,因为两个数相加,是不能改变左操作数或右操作数的值的,所以返回值类型为MyInt,而不是MyInt&

2.重载相加操作符+的非成员函数为:MyInt operator+(const MyInt &a, const MyInt &b)

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);friend istream& operator>>(istream &cin, MyInt &a);friend MyInt operator+(const MyInt &a, const MyInt &b);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}istream& operator>>(istream &cin, MyInt &a)
{cin >> a.m_num;return cin;
}MyInt operator+(const MyInt &a, const MyInt &b)
{return MyInt(a.m_num + b.m_num);
}int main(int argc, char *argv[])
{MyInt a(1);MyInt b(2);cout << "a + b = " << a + b << endl;return 0;
}

重载复合赋值操作符+=

1.返回值类型为:MyInt&,因为x += y;是要改变左操作数x,并且返回的还是被修改后的x的

2.重载复合赋值操作符+=的非成员函数为:MyInt& operator+=(MyInt &a, const MyInt &b)

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);friend istream& operator>>(istream &cin, MyInt &a);friend MyInt& operator+=(MyInt &a, const MyInt &b);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}istream& operator>>(istream &cin, MyInt &a)
{cin >> a.m_num;return cin;
}MyInt& operator+=(MyInt &a, const MyInt &b)
{a.m_num += b.m_num;return a;
}int main(int argc, char *argv[])
{MyInt a(1);MyInt b(2);a += b;cout << "after a += b, a = " << a << endl;return 0;
}

重载相等操作符==,不等操作符-=

1.如果类中有一个操作,是确认该类中的两个对象是否相等的,我们应该将该函数名定义为operator==,而不是定义一个命名函数。因为我们更习惯于用==操作符来比较两个对象是否相等。

2.对于一个类,如果我们定义了operator==,也应该定义operator!=。

3.定义了operator==的类更容易与标准库一起使用。例如:find,默认使用了==操作符,如果定义了==,那么这么算法可以无须任何特殊处理而用于该类类型。

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);friend istream& operator>>(istream &cin, MyInt &a);friend bool operator==(const MyInt &a, const MyInt &b);friend bool operator!=(const MyInt &a, const MyInt &b);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}istream& operator>>(istream &cin, MyInt &a)
{cin >> a.m_num;return cin;
}bool operator==(const MyInt &a, const MyInt &b)
{return a.m_num == b.m_num;
}bool operator!=(const MyInt &a, const MyInt &b)
{return a.m_num != b.m_num;
}int main(int argc, char *argv[])
{MyInt a(1);MyInt b(2);MyInt c(1);if (a == b)cout << "a == b" << endl;else cout << "a != b" << endl;if (a == c)cout << "a == c" << endl;else cout << "a != c" << endl;if (a != b)cout << "a != b" << endl;else cout << "a == b" << endl;if (a != c)cout << "a != c" << endl;else cout << "a == c" << endl;return 0;
}

重载赋值操作符=

1.C++是允许类类型对象给同类型的其他对象赋值的,具体可查看我写的这篇文章里利用等号法创建对象的介绍C++面向对象编程之二:构造函数、拷贝构造函数、析构函数,这里不再累赘。类赋值操作符形参的数据类型为该类类型,通常形参为该类类型的const引用,或该类的非const引用或类类型或更多的其他数据类型(例如:MyInt类中,那么该类的赋值操作符形参可为const MyInt &,或MyInt &或MyInt)。如果我们没有定义赋值操作符=,那么编译器将为该类提供一个。所以,类赋值操作符=必须是类成员函数,以便编译器可以知道是否需要为该类提供一个。

2.重载赋值操作符=的返回值类型必须是*this的引用(即左操作符的引用),这样子返回是跟内置数据类型的赋值是一致的。因为赋值返回*this的引用,这样子就不需要创建一个该类的一个临时对象,然后将临时对象返回,然后再调用该类的拷贝构造函数,将该临时对象的值拷贝后,又释放该临时对象,这一系列的系统开销,从而提高代码的执行效率。

#include 
using namespace std;class MyInt
{friend ostream& operator<<(ostream &cout, const MyInt &a);friend istream& operator>>(istream &cin, MyInt &a);public:MyInt():m_num(0){}MyInt(const int num):m_num(num){}MyInt& operator=(const MyInt &b){m_num = b.m_num;return *this;}private:int m_num;
};ostream& operator<<(ostream &cout, const MyInt &a)
{cout << a.m_num;return cout;
}istream& operator>>(istream &cin, MyInt &a)
{cin >> a.m_num;return cin;
}int main(int argc, char *argv[])
{MyInt a(1);MyInt b = a;cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}

好了,关于重载操作符,篇幅比较长,本文对重载操作符(<<,>>,+,+=,==,!=,=)进行了讲解,还有一些比较重要的操作符,比如前++,后++,前--,后--,下标操作符[]等,还没有介绍,就放到下一篇博文吧!

相关内容

热门资讯

美丽的安家沟四年级作文(精选... 美丽的安家沟四年级作文 篇一安家沟是我家乡一个美丽的小村庄,它位于山脚下,四周环绕着郁郁葱葱的树林,...
四年级童话作文(精选6篇) 四年级童话作文 篇一:《小兔子的冒险之旅》从前有一只可爱的小兔子,它叫小白。小白住在一个美丽的森林里...
小家庭大变化四年级作文【经典... 小家庭大变化四年级作文 篇一四年级的我,经历了一次小家庭的大变化。这个变化发生在我上小学的第一年,让...
小小动物园四年级作文【最新6... 小小动物园四年级作文 篇一我的家乡有一个小小动物园,里面有各种各样的动物,每次我去都会看到很多有趣的...
我的乐园四年级下册作文200... 我的乐园四年级下册作文200字 篇一我的乐园我家的后院是我的乐园,这里有我最喜欢的花草和小动物。每天...
春天的小学四年级作文300字... 春天的小学四年级作文300字 篇一:春天里的花海春天是一个美丽的季节,它给大地披上了五彩斑斓的外衣。...
我学会了洗衣服四年级作文(精... 我学会了洗衣服四年级作文 篇一我学会了洗衣服在我四年级的时候,我学会了洗衣服。这是一个令人兴奋又有趣...
我的朋友四年级作文500字【... 我的朋友四年级作文500字 篇一:快乐的小伙伴我有一个非常好的朋友,他叫小明。小明是我的同班同学,也...
写快乐的春节小学作文【最新6... 写快乐的春节小学作文 篇一快乐的春节春节是我最喜欢的节日,因为在这个特别的日子里,我可以和家人一起欢...
管门口的金毛四年级作文【推荐... 管门口的金毛四年级作文 篇一我家门口有一只非常可爱的金毛犬,它是我们的守门员,每天都在门口忠实地守卫...
我的压岁钱小学四年级作文【通... 我的压岁钱小学四年级作文 篇一我的压岁钱春节是我最喜欢的节日,因为我可以收到压岁钱。每年过年的时候,...
致那份友谊小学作文(推荐3篇... 致那份友谊小学作文 篇一友谊的力量亲爱的友谊小学的老师们和同学们:我是一名来自友谊小学的学生,今天我...
为自己喝彩小学生作文【精简6... 为自己喝彩小学生作文 篇一我是一名小学生,每天都在学校度过快乐的时光。我喜欢上学,因为学校给了我很多...
我生病了小学作文【精简6篇】 我生病了小学作文 篇一我生病了前几天,我不知道怎么了,突然感觉身体不舒服。我感到头晕目眩,喉咙痛得像...
新学期新打算小学作文450字... 新学期新打算篇一:我要努力学习新的学期开始了,我制定了新的打算,那就是要努力学习。我相信只有努力学习...
我学会了西红柿炒鸡蛋小学作文... 我学会了西红柿炒鸡蛋小学作文 篇一我学会了西红柿炒鸡蛋上周,我学会了一道简单又美味的菜——西红柿炒鸡...
花朵的小学作文【最新3篇】 花朵的小学作文 篇一花朵的奇妙世界花朵是大自然的美丽礼物,它们以各种各样的颜色和形状装点着我们的环境...
小学生赏花的作文【通用4篇】 小学生赏花的作文 篇一春天是一个充满美丽花朵的季节,我非常喜欢春天。每当春天来临,我就会和家人一起去...
中秋之夜小学生作文【优选3篇... 中秋之夜小学生作文 篇一中秋之夜,月亮圆圆的,像一块白玉挂在天空中。我和爸爸妈妈一起出门,欣赏美丽的...
油面筋塞肉小学作文(推荐3篇... 油面筋塞肉小学作文 篇一我喜欢吃美食,尤其是一些特色的小吃。最近,我发现了一种非常好吃的小吃,那就是...