8、操作符重载

友元

  • 可以通过friend关键字,把一个全局函数、另一个类的成员函数或者另一个类整体,声明为授权类的友元
  • 友元拥有访问授权类任何非公有成员的特权
  • 友元声明可以出现在授权类的公有、私有或者保护等任何区域不受访问控制限定符的约束
  • 友元不是成员,其作用域并不隶属于授权类,也不拥有授权类类型的this指针。

操作符标记和操作符函数

双目操作符表达式

L#R

成员函数形式:L.operator#(R)

  • 左操作数是调用对象,右操作数是参数对象

全局函数形式:operator#(L,R)

  • 左操作数是第一个参数,右操作数是第二个参数

单目操作符表达式

#O/O#

  • 成员函数形式:O.operator#()
  • 全局函数形式:operator#(O)

三目操作符表达式: F#S#T

三目操作符无法重载

经典双目操作符

运算类双目操作符:+、-、*、/等

  • 左右操作数均可以为非常左值、常左值或右值
  • 表达式的结果为右值
c++ 复制代码
// 运算类双目操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
    void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
    // 成员形式操作符函数
//  Human operator+(const Human& r) const {
//      return Human(this->m_age+r.m_age, (this->m_name+"+"+r.m_name).c_str());
//  }
private:
    int m_age;
    string m_name;
    friend Human operator+(const Human& l,const Human& r); // 友元声明
};
// 全局形式操作符函数
Human operator+(const Human& l, const Human& r){
    return Human(l.m_age+r.m_age, (l.m_name+"+"+r.m_name).c_str());
}

int main(void){
    Human a(22,"张飞"), b(20,"赵云");  // 非常左值
    const Human c(25,"关羽"), d(32,"马超"); // 常左值

    Human res = a + b; // ==> a.operator+(b)  或 operator+(a,b)
    res.getInfo();
    
    res = c + d; // ==> c.operator+(d)  或 operator+(c,d)
    res.getInfo();
    
    res = Human(45,"黄忠") + Human(35,"刘备"); // ==> Human(45,"黄忠").operator+(Human(35,"刘备")) 或 
                                         //     operator+(Human(45,"黄忠"),Human(35,"刘备"))
    res.getInfo();

    return 0;
}

赋值类双目操作符:=、+=、-=、*=、/=等

  • 右操作数可以为非常左值、常左值或右值,但左操作数必须为非常左值
  • 表达式结果为左操作数本身(而非副本)
c++ 复制代码
// 赋值类双目操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
    void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
    // 成员形式操作符函数
    Human& operator+=(const Human& r){
        this->m_age = this->m_age + r.m_age;
        this->m_name = this->m_name + "+" + r.m_name;
        return *this;
    }
private:
    int m_age;
    string m_name;
    friend Human operator+(const Human& l,const Human& r); // 友元声明
};

 全局形式操作符函数
//Human& operator+(Human& l, const Human& r){
//       l->m_age = l->m_age + r.m_age;
//       l->m_name = l->m_name + "+" + r.m_name;
//       return *l;
//}

int main(void){
    Human a(22,"张飞"), b(20,"赵云");  // 非常左值
    const Human c(25,"关羽"), d(32,"马超"); // 常左值

    ((a+=b)+=c)+=Human(45,"黄忠");
    a.getInfo();

    return 0;
}

比较类双目操作符:>、<、==、<=、>=等

  • 左右操作数为非常左值、常左值或右值
  • 表达式结果为 bool
c++ 复制代码
// 比较类双目操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
    void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
//    // 成员形式操作符函数
//    bool operator==(/*const Human* this */ const Human& that)const{
//        return this->m_age==that.m_age && this->m_name==that.m_name;
//    }
//    bool operator!=(/*const Human* this */ const Human& that)const{
      return this->m_age!=that.m_age || this->m_name!=that.m_name;
//        return !(*this==that);//使用operator==
//    }
private:
    int m_age;
    string m_name;
    friend bool operator==(const Human& l, const Human& r); // 友元声明
    friend bool operator!=(const Human& l, const Human& r); // 友元声明
};
// 全局形式操作符函数
bool operator==(const Human& l, const Human& r){
    return l.m_age==r.m_age && l.m_name==r.m_name;
}
bool operator!=(const Human& l, const Human& r){
//      return l.m_age!=r.m_age || l.m_name!=r.m_name;
    return !(l==r);//使用operator==
}

int main(void){
    Human a(22,"张飞"), b(20,"赵云");  // 非常左值
    const Human c(25,"关羽"), d(32,"马超"); // 常左值

    cout << (a == b) << endl; //0 ==> a.operator==(b) 或者 ...
    cout << (a != b) << endl; //1 ==> a.operator!=(b) 或者 ...
    cout << (c == d) << endl; //0 ==> c.operator==(d) 或者 ...
    cout << (c != d) << endl; //1 ==> c.operator!=(d) 或者 ...
    cout << (Human(45,"黄忠") == Human(35,"刘备")) << endl; //0 ==> Human(45,"黄忠").operator==(Human(35,"刘备")) 或者 ...
    cout << (Human(45,"黄忠") != Human(35,"刘备")) << endl; //1 ==> Human(45,"黄忠").operator!=(Human(35,"刘备")) 或者 ...

    return 0;
}

经典单目操作符

运算类单目操作符:-、~、!等

  • 操作数为非常左值、常左值或右值
  • 表达式的结果为右值
c++ 复制代码
// 运算类单目操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
    void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
    // 成员形式操作符函数
    Human operator-(/* const Human* this */)const{
        return Human(-this->m_age,("-"+this->m_name).c_str());
    }
private:
    int m_age;
    string m_name;
//    friend Human operator-(const Human& l); // 友元声明
};

//Human operator-(const Human& l){
//    return Human(-l.m_age,("-"+l.m_name).c_str());
//}
int main(void){
    Human a(22,"张飞"), b(20,"赵云");  // 非常左值
    const Human c(25,"关羽"), d(32,"马超"); // 常左值

    Human res = -a; // ==> a.operator-() 或  ...
    res.getInfo();//姓名:-张飞, 年龄:-22

    res = -c; // ==> c.operator-() 或  ...
    res.getInfo();//姓名:-关羽, 年龄:-25

    res = -Human(45,"黄忠"); // ==> Human(45,"黄忠").operator-() 或 ...
    res.getInfo();//姓名:-黄忠, 年龄:-45

    return 0;
}

前自增减类单目操作符: 前++、前--

  • 操作数为非常左值
  • 表达式的结果为操作数本身(而非副本)

后自增减类单目操作符: 后+ +、后--

  • 操作数为非常左值
  • 表达式的结果为右值,且为自增减以前的值
c++ 复制代码
// 自增减类单目操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
    void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }
    //成员形式操作符函数
//    //   前++   ++a
//    Human& operator++(/* Human* this */){
//        this->m_age+=1;   // 直接加1
//        this->m_name+="a";   // 直接加
//        return *this;
//    }
    后++  a++
//    Human operator++(/* Human* this */int){
//        Human old = *this;   // 备份原来的值
//        this->m_age+=1;  // 直接加1
//        this->m_name+="a";   // 直接加
//        return old;  // 返回原来的值
//    }
private:
    int m_age;
    string m_name;
    friend  Human& operator++(Human&  l); // 友元声明
    friend  Human operator++(Human&  l,int); // 友元声明
};
    //   前++   ++a
    Human& operator++(Human&  l){
        l.m_age+=1;   // 直接加1
        l.m_name+="a";   // 直接加
        return l;
    }
    //    后++  a++
    Human operator++(Human& l,int){
        Human old = l;   // 备份原来的值
        l.m_age+=1;  // 直接加1
        l.m_name+="a";   // 直接加
        return old;  // 返回原来的值
    }

int main(void){
    Human a(22,"张飞"), b(20,"赵云");  // 非常左值
    const Human c(25,"关羽"), d(32,"马超"); // 常左值


    //姓名:张飞a, 年龄:23
    (++a).getInfo(); // a.operator++() 或 ...

    //姓名:赵云, 年龄:20
    (/*|...|*/b++).getInfo(); // b.operator++(0) 或 ...

    //姓名:赵云a, 年龄:21
    b.getInfo();

    return 0;
}

其他操作符

输出操作符: <<

  • 左操作数为非常左值形式的输出流(ostream)对象,右操作数为左值或右值
  • 表达式的结果为左操作数本身(而非副本)
  • 左操作数的类型为ostream,若以成员函数形式重载该操作符,就应将其定义为ostream类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符

ostream& operator< < (ostream& os, const RIGHT& right) { ...}

c++ 复制代码
// 输入/输出流操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
/*  void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }*/
    // 成员形式操作符函数
private:
    int m_age;
    string m_name;
    friend ostream& operator<<(ostream& os, const Human& that);
    friend istream& operator>>(istream& is, Human& that);
};
// 全局形式操作符函数
ostream& operator<<(ostream& os, const Human& that){
    os << "姓名:" << that.m_name << ", 年龄:" << that.m_age;
    return os;
}

//istream& operator>>(istream& is, Human& that){
//    is >> that.m_name >> that.m_age;
//    return is;
//}


int main(void){
    Human a(22,"张飞");  // 非常左值
    const Human b(20,"赵云");  // 常左值

    cout << a << endl; // cout.operator<<(a) 或 operator<<(cout,a)
    cout << b << endl; // cout.operator<<(b) 或 operator<<(cout,b)
    cout << Human(45,"黄忠") << endl; // cout.operator<<(Human(45,"黄忠")) 或 operator<<(cout,Human(45,"黄忠"))

//    cin >> a; // cin.operator>>(a) 或 operator>>(cin,a)
    cout << a << endl;

    return 0;
}

输入操作符: >>

  • 左操作数为非常左值形式的输入流(istream)对象,右操作数为非常左值
  • 表达式的结果为左操作数本身(而非副本)
  • 左操作数的类型为istream,若以成员函数形式重载该操作符,就应将其定义为istream类的成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数形式重载该操作符

istream& operator>> (istream& is, RIGHT& right) { ...}

c++ 复制代码
// 输入/输出流操作符函数
#include <iostream>
using namespace std;

class Human{
public:
    Human(int age = 0, const char* name="匿名"):m_age(age),m_name(name){
        // [int m_age = age;]
        // [string m_name(name);]
    }
/*  void getInfo(){
        cout << "姓名:" << m_name << ", 年龄:" << m_age << endl;
    }*/
    // 成员形式操作符函数
private:
    int m_age;
    string m_name;
    friend ostream& operator<<(ostream& os, const Human& that);
    friend istream& operator>>(istream& is, Human& that);
};
// 全局形式操作符函数
ostream& operator<<(ostream& os, const Human& that){
    os << "姓名:" << that.m_name << ", 年龄:" << that.m_age;
    return os;
}

istream& operator>>(istream& is, Human& that){
    is >> that.m_name >> that.m_age;
    return is;
}


int main(void){
    Human a(22,"张飞");  // 非常左值

    cin >> a; // cin.operator>>(a) 或 operator>>(cin,a)
    cout << a << endl;

    return 0;
}

下标操作符:[]

  • 一般用于在容器类型中以下标方式获取数据元素
  • 非常容器的元素为非常左值,常容器的元素为常左值

类型转换操作符

若源类型是基本类型,目标类型是类类型,则只能通过类型转换构造函数实现自定义类型转换

c++ 复制代码
class 目标类型{
	目标类型(const 源类型& src){ ... }
}

若源类型是类类型,目标类型是基本类型,则只能通过类型转换操作符函数 实现自定义类型转换

c++ 复制代码
class 源类型{
	operator 目标类型(void) const { ...}
}

若源类型和目标类型都是类类型 (而非基本类型) ,则既可以通过类型转换构造函数也可以通过类型转换操作符函数实现自定义类型转换,但不要两者同时使用

若源类型和目标类型都是基本类型,则无法实现自定义类型转换,基本类型间的类型转换规则完全由编译器内置

类型转换构造函数和类型转换操作符函数

c++ 复制代码
// 类型转换构造函数和类型转换操作符函数
#include <iostream>
using namespace std;

class Integer{
public:
    Integer(int i):m_i(i){
        //【int m_i = i;】
        cout << "Integer类的类型转换构造函数被调用" << endl;
    }
    operator int(/* const Integer* this */)const{
        cout << "Integer类的类型转换操作符函数被调用" << endl;
        return this->m_i;
    }
private:
    int m_i;
};


int main(void){
    int n = 666;

    // int --> Integer(基本类型-->类类型)
    Integer ix = n; // 定义匿名Integer对象,利用匿名Integer对象.Integer(n) --> 触发类型转换构造函数
                    // Integer ix = n.operator Integer() --> int类中没有一个operator Integer(走不通)                

    // Integer --> int(类类型 --> 基本类型)
    int m = ix; // 定义匿名int对象,利用匿名int对象.int(ix)-->int类中没有一个形参是Integer类型的构造函数(走不通)
                // int m = ix.operator int() --> 触发类型转换操作符函数
    return 0;
}
c++ 复制代码
// 类型转换构造函数和类型转换操作符函数
#include <iostream>
using namespace std;

class Dog; // 短式声明/前置声明

class Cat{
public:
    Cat(const char* name):m_name(name){
        // [string m_name(name);]
    }
    void talk() {
        cout << m_name << ": 喵喵~~~" << endl;
    }
    operator Dog(/* const Cat* this */)const; // 声明
private:
    string m_name;
    friend class Dog; // 友元声明
};

class Dog{
public:
    Dog(const char* name):m_name(name){
        // [string m_name=name;]
    }
/*    Dog(const Cat& that):m_name(that.m_name){ // 类型转换构造(定制了Cat-->Dog的转换规则)
        //【string m_name = that.m_name;】
        cout << "Dog类的类型转换构造函数被调用" << endl;
    }*/
    void talk() {
        cout << m_name << ": 汪汪~~~" << endl;
    }
private:
    string m_name;
};
Cat::operator Dog(/* const Cat* this */)const{ // 定义
    cout << "Cat类的类型转换操作符函数被调用" << endl;
    return Dog(this->m_name.c_str());
}


int main( void ) {
    Cat smallwhite("小白"); 

    Dog bigyellow = smallwhite;  // 定义匿名Dog类对象,利用匿名Dog类对象.Dog(smallwhite)-->触发Dog类的类型转换构造函数
                                 // Dog bigyellow = smallwhite.operator Dog() --> 触发Cat类的类型转换操作符函数
    return 0;
}

操作符重载的局限

不是所有的操作符都能重载,以下操作符不能重载

  • 作用域限定操作符(::)
  • 直接成员访问操作符(.)
  • 条件操作符(?:)
  • 字节长度操作符(sizeof)
  • 类型信息操作符(typeid)

无法重载所有操作数均为基本类型的操作符: 如实现 1+1=3

c++的前++和后++

  1. 在C语言中
    前++: 先加1,再使用 ++a
    后++: 先使用,再加1 a++
  2. 在C++语言中,不管是前++还是后++,都是直接加1(内部原理和C语言并不同)
    但是C++希望用户感觉和C一样

c++的前++和后++的区别

  • 表达式方式区别:i++是先取变量i,再将变量i值+1;而++i是先将变量i值+1,再取变量i。在循环遍历容器变量时,这两种方式的结果都是一样的,但是,本质的效率上有很大的区别,下面介绍另一种效率区别。
  • 效率:两种方式iterator遍历的次数是相同的,但在STL中效率不同,前++返回引用,后++返回一个临时对象,因为iterator是类模板,使用 it++这种形式要返回一个无用的临时对象,而it++是函数重载,所以编译器无法对其进行优化,所以每遍历一个元素,你就创建并销毁了一个无用的临时对象。C++的标准库,还有符合标准C++的教材,除了特殊需要和对内置类型外,基本都是使用++it来进行元素遍历的,不管是源代码还是教材中都是如此。

下面是标准库源码:

相关推荐
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
CYBEREXP20084 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos4 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama5 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou5 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生975 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns5 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns5 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库6 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构