自定义的类,不支持常用的操作符号,如果需要使用一些操作符,则该类必须对相应的操作符进行重载。
如自定义类Person:
#include
class Person
{
private:
int m_age;
std::string m_name;
public:
Person(int age, std::string name);
};
Person p(12,"zhangsan");
那么当要通过cout
输出p时,可以这样做:
std::cout << p << std::endl;
然而,编译器发现p不支持<<
运算符,从而报错:
no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘Person’)
如果需要实现该功能,则需要Person类对<<
进行重载。
运算符重载也是多态的一种形式,如*
可以表示指针,可以表示解除引用操作符,可以表示乘法运算,c++编译器将根据使用场景来决定采用哪种操作。
可以有两种方式实现运算符重载:
运算符重载格式如下:
operatorop(argument-list);
operator
关键字表示重载运算符,op表示要重载的运算符;
argument-list
表示参数;
在运算符表示法中,左侧的对象表示调用对象,因此将作为隐式参数传入operatorop()
函数中,右侧的对象则显式地作为形参被传入。
此限制针对友元函数的重载,如果是成员函数重载,则操作符左边对象被作为调用对象传入。
sizeof 运算符
. 成员运算符
.* 成员指针运算符
:: 作用域解析符
?:条件运算符
typeid RTTI运算符
const_cast 强制类型转换运算符
dynamic_cast 强制类型转换运算符
reinterpret_cast 强制类型转换运算符
static_cast 强制类型转换运算符
= 赋值运算符(重载解决浅拷贝问题)
() 函数调用运算符
[] 下标运算符
-> 指针访问类运算符
+
运算符//Time.h
#ifndef _TIME_H_
#define _TIME_H_
class Time
{
private:
int hour;
int minute;
public:
Time();
~Time();
Time(int h,int m);
Time addTime(const Time &t1) const;
Time operator+(const Time &t) const;
int getHour() const;
int getMinute() const;
};
#endif
//Time.cpp
#include
#include "Time.h"
Time::Time(){
hour = 0;
minute = 0;
}
Time::~Time(){
//blank;
}
Time::Time(int h,int m) {
hour = h;
minute = m;
}
//之所以返回Time对象,是为了支持链式操作
Time Time::addTime(const Time & t) const {
Time temp;
temp.minute = (minute + t.minute) % 60;
temp.hour = hour + t.hour + (minute + t.minute) / 60;
return temp;
}
//重载+运算符
Time Time::operator+(const Time &t) const {
Time temp;
temp.minute = (minute + t.minute) % 60;
temp.hour = hour + t.hour + (minute + t.minute) / 60;
return temp;
}
int Time::getHour() const {
return hour;
}
int Time::getMinute() const {
return minute;
}
//main文件中
#include
#include "Time.h"
int main()
{
using namespace std;
Time t1;//call Time()
Time t2(2,25);//call Time(int,int)
Time t3(4,45);
cout << "t1:" << t1.getHour() << "h" << t1.getMinute() << "m" << endl;
cout << "t2:" << t2.getHour() << "h" << t2.getMinute() << "m" << endl;
cout << "t3:" << t3.getHour() << "h" << t3.getMinute() << "m" << endl;
Time t4 = t2.addTime(t3);//call addTime(Time)
cout << "t4:" << t4.getHour() << "h" << t4.getMinute() << "m" << endl;
Time t5 = t4 + t2;//call operator+(Time)
cout << "t5:" << t5.getHour() << "h" << t5.getMinute() << "m" << endl;
return 0;
}
/*
运行结果:
@ubuntu:~/workspace/C++/chapter11$ g++ Time.h Time.cpp client.cpp -o time
@ubuntu:~/workspace/C++/chapter11$ ./time
t1:0h0m
t2:2h25m
t3:4h45m
t4:7h10m
t5:9h35m
*/
在重载+
运算符时,有一个细节:返回值为Time,这主要是由于两个因素:
Time &n
呢?在重载操作符时,大部分情况下返回一个引用比返回一个对象更合适,但是这里之所以返回Time对象,是因为该对象是在方法内部实例化的,也就是说该对象的作用域为当前方法,当方法执行完毕,对象就被析构了,如果返回一个引用,那么这个引用将会指向一个不存在的对象。而返回对象则不同,编译器会在删除该对象之前调用其拷贝构造函数得到一个新的匿名对象,并将该匿名对象赋给接受它的对象。
*
运算符//重载*运算符
Time Time::operator*(int i) const {
Time temp;
temp.hour = this->hour * i + (this->minute * i) / 60;
temp.minute = (this->minute * i) % 60;
return temp;
}
以上两个示例是通过成员函数对运算符进行了重载,那么可以通过成员函数对<<
运算符进行重载呢?如通过重载<<
运算符,就可以直接使用如下代码输出了:
std::cout << t << std::endl;
如果使用成员函数进行重载,根据运算符表示法,运算符左边为调用对象,右边为参数,而左边的对象是std::cout
,所以,这种方案是不可行的。除非使用如下方式调用:
t << std::cout;
但这种方式却不符合c++的编程习惯。
对于二元运算符的重载,使用友元函数就可实现。下面就看看看,如何通过友元函数对运算符进行重载。
c++中对类的访问通过访问修饰符进行控制,类对象不能能访问private
修饰的变量和函数。有时候这种限制过于严格,因此,c++提供了另一种形式的访问权限——友元。
通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。可以将友元理解为“类的好朋友”。简单来说,一个类的友元可以访问其私有属性和方法。
friend
:friend void func();
void func()
{
......
}
当需要为类重载二元运算符时,就需要使用友元函数来实现。
还是以前面的Time类为例,来看看如何使用友元函数进行重载。
+
运算符//Time2.h
class Time
{
private:
int hour;
int minute;
public:
Time();
Time(int hour,int minute);
int getHour();
int getMinute();
public:
friend Time operator+(Time &t1,Time &t2);//使用友元函数重载+
};
//Time2.cpp
#include "Time2.h"
Time::Time(){
hour = 0;
minute = 0;
}
Time::Time(int hour,int minute) {
this->hour = hour;
this->minute = minute;
}
int Time::getHour(){
return hour;
}
int Time::getMinute() {
return minute;
}
//友元函数不属于类,故不需要Time::限定
Time operator+(Time &t1,Time &t2) {
Time temp;
temp.hour = t1.hour + t2.hour + (t1.minute+ t2.minute) / 60;
temp.minute = (t1.minute + t2.minute) % 60;
return temp;
}
#include
#include "Time2.h"
int main()
{
using namespace std;
Time t1;//call Time()
Time t2(2,25);//call Time(int,int)
Time t3(4,45);
cout << "t1:" << t1.getHour() << "h" << t1.getMinute() << "m" << endl;
cout << "t2:" << t2.getHour() << "h" << t2.getMinute() << "m" << endl;
cout << "t3:" << t3.getHour() << "h" << t3.getMinute() << "m" << endl;
Time t4 = t2 + t3;//call operator+()
cout << "t4:" << t4.getHour() << "h" << t4.getMinute() << "m" << endl;
return 0;
}
*
运算符//Time2.h
class Time
{
//.....
public:
friend Time operator*(Time &t1, int i);
};
//Time2.cpp
Time operator*(Time &t,int i) {
Time time;
time.hour = t.hour * i + (t.minute * i) / 60;
time.minute = (t.minute * i) % 60;
return time;
}
<<
运算符//Time2.h
#include
class Time
{
//......
public:
friend std::ostream & operator<<(std::ostream &os,const Time & t);
};
//Time2.cpp
std::ostream & operator<<(std::ostream &os,const Time &t) {
os << t.hour << "h" << t.minute << "m" << std::endl;
return os;
}
//main()函数文件中:
int main()
{
Time t(5,28);
std::cout << t << endl;
return 0;
}
返回ostream&是为了支持链式调用。
通过在Time类中对同一个操作符使用两种方式重载后,可以发现这两种方式的特点:
this
指针隐式传递,另一个操作书则作为函数参数显式地传递,对于友缘函数来说,两个操作数都作为参数来传递。不管使用何种方式,只能选择一种,如果对同一个操作符分别使用了两种方式都进行了重载,则将出现二义性错误。