运算符重载基础知识

固定用法:

类内声明:

bool operator>=(const Student&) const;

初始化:

bool Student::operator>=(const Student& stu) const
{//常成员函数,功能:比较两个对象的数据成员dScore大小
    return this->dScore >= stu.dScore;
}

可以看到返回值是bool类型,即true或false,所以在后面的if语句中可以直接使用>=

if(s1 >= s2)
    cout << s1.GetName() << " " << s1.GetScore() << endl;
else
    cout << s2.GetName() << " " << s2.GetScore() << endl;

注意:

  • 只能重载C++中已有的运算符,不能创建新的运算符
  • 重载后不改变运算符优先级顺序(比如乘除和加减)
  • 重载后不改变运算符的结合性
  • 重载后不改变操作数的个数(比如-,可以a-b,也可以-a)
  • 重载后不改变运算符原有的语义
  • 重载时的操作对象至少有一个应该是类对象或其引用(否则没必要重载)


重载的两种方式

  • 重载为类的成员函数

1、当运算符是双目运算符时
左操作数 运算符 右操作数;
编译时:

s1>=s2;//与下面式子等价
s1.operator>=(s2);

2、当运算符是单目运算符时
类内定义:

Time operator++(); //前置自增
    Time operator++(int); //后置自增

返回的才是当前要使用的:

Time Time::operator++()//实现前置自增 ++a;
{
    nSecond++;
    if(nSecond == 60) {nMinute++; nSecond = 0;}
    if(nMinute == 60) {nHour++; nMinute = 0;}
    nHour %= 24;
    return *this;//返回自增计算之后的结果 ;
}
Time Time::operator++(int)//实现后置自增 a++;
{
    Time t = *this;//先把自增前的结果存储到t中
    ++(*this);
    //然后对this指针所指的进行自增,这里调用了上面前置自增
    return t;// 返回自增之前的t;
}

  • 重载为类的友元函数
    因为友元函数不能通过对象调用,所以运算符的操作数全部作为运算符重载函数的参数传入。

1.运算符是双目运算符:

作为函数的参数传入,并且顺序一定是(左操作数,右操作数)。

类内:

class Student
{
public:
	friend bool operator>=(const Student&, const Student&);
};

类外定义:

bool operator>=(const Student& stu1, const Student& stu2)
{
    return stu1.dScore >= stu2.dScore;
}

注:

  • 双目运算符通常重载为类的友元函数
  • 单目运算符通常重载为类的成员函数
  • 必须重载为友元函数:“ << ”、“ >> ”、类型转换运算符
  • 必须重载为成员函数:赋值运算符“ = ”、下标运算符“ [] ”、成员访问运算符“ -> ”、函数调用运算符“ () ”
  • 访问运算符“ . ”,指针运算符“ * ”,域运算符“ :: ”,条件运算符“ ? : ”和sizeof运算符不能被重载。



**

为什么“<<” “>>” “类型转换运算符”要重载为友元函数?

因为这类运算符的参数通常有一个是这个类的对象,另一个不是这个类的对象,所以一定要重载为这个类的友元函数。
格式举例:

class Time(){
public:
	friend istream& operator>>(istream&,Time&);
	...
};

istream& operator>>(istream& is,Time& t){
	is>>t.hour;
	return is;
}

//上面的is可以理解为是istream类的一个对象,
//同理,当我们重载输出时。
// 即<<重载的时候 可以定义一个ostream类的对象 os
//用法和函数基本相同;




关系运算符的重载

C++中一共有6个关系运算符(> ;< ;>= ;<= ;== ;!= )
当重载关系运算符的时候一定要成对重载【>和<】【>=和<=】【==和!=】
此时,我们就可以把一个运算符的实现委托给另一个已经定义的运算符。
如下面的例子

bool operator>(const Student& stu1, const Student& stu2)
{
    return stu1.dScore > stu2.dScore;
}
bool operator<(const Student& stu1, const Student& stu2)
{
    return stu2 > stu1; //调用楼上的 > 函数
}


下标运算符的重载

重载格式:

类型说明符& operator[](参数)

那么,为什么要对下标运算符重载呢?

  • ”对象[下标]“ 这种使用方法更符合习惯
  • 在[ ]使用中增加下标越界检查,使得[ ]使用更为安全,看下面的例子
int main(){
	string s{"hello"}
	s[5] = 'A';
	cout<<s[5]<<endl;
}

此时程序的输出为A,虽然赋值成功,但是在s[5]这里存在下标越界的问题(C/C++默认不自动进行越界检查),对此,下标运算符的重载就显得尤为重要。

注意

  • [ ]运算符只能重载为类的成员函数
  • 当函数返回类型为引用的时候不能返回函数中的动态局部变量!(如下例)
char& String::operator[](int index)
{
    static char temp;
    //这里的temp已经设置成了static类型,但如果没有static则为动态局部变量,是无法返回的
    //temp = '\0';
    if(index >= strlength || index < 0) //下标越界判断
    {
        cout << "数组下标越界!请检查下标值。" << endl;
        return temp; //下标越界,返回空字符
    }
    return *(str + index);
}


函数调用运算符的重载

将“()”重载到类内,可以使对象像调用函数一样使用”()“
必须重载为类的成员函数
如下:

public:
    Linear(double a, double b):a_(a), b_(b){}
    double operator()(double x)
    {
        return a_ * x + b_;
    }


int main()
{
    Linear f(2, 3); //定义线性函数f(x) = 2x + 3
    double x = 2;
    cout << "f(" << x << ")=" << f(2) << endl;//这里是调用
    return 0;
}



类的转换(类型转换构造函数)

当我们使用基本类型的时候如int和double直接可以灵活的进行转换,那么如果需要将Teacher类的对象转换成Student类的对象呢?
这时候我们就需要 类型转换构造函数
如果函数只需要一个参数,并且这个参数不是当前类类型的参数,那么这个函数就是类型转换构造函数

下面的例子是将一个秒数(其他类的对象)转换成Time类的对象。

class Time
{
public:
    Time(long long = 0); // 类型转换构造函数
    void ShowTime();
private:
    int nHour;
    int nMinute;
    int nSecond;
};

Time::Time(long long sec)
{
    sec = sec % (24 * 3600); 
    nHour = sec / 3600;
    sec %= 3600;
    nMinute = sec / 60;
    nSecond = sec % 60;
}

下面的例子是将Time类的对象转换成秒数(其他类的对象)

class Time
{
public:
    Time(int = 0, int = 0, int = 0);
    operator int();//类型转换构造函数
    void ShowTime();
private:
    int nHour;
    int nMinute;
    int nSecond;
};

Time::operator int()
{
    return nHour * 3600 + nMinute * 60 + nSecond;
}

本台插播:显式类型转换和隐式类型转换

如果已经对类型转换构造函数进行定义,那么在两个类的对象进行转换的时候可以使用显式类型转换和隐式类型转换,两种方法如下:

类的定义如下

class Time
{
public:
    Time(long long = 0); // 类型转换构造函数
    void ShowTime();
private:
    int nHour;
    int nMinute;
    int nSecond;
};

显式类型转换:

int main(){
	Time t = Time(n);
	return 0;
}

隐式类型转换:

int main(){
	Time t;
	t = 2000;
	return 0;
}

注意:我们要尽量避免使用隐式类型转换,可以在定义类型转换构造函数的时候在语句前面加上关键字:explicit ; 此时,在程序中,只允许使用强制类型转换,而不能使用自动类型转换。
如下:

class Time{
public:
	explicit Time(long long = 0);
};

OVER

GL&HF