C++从入门到放弃之:操作符重载看过必会

操作符重载

  • 基本概念
    操作符重载就是一些具有特殊名字的函数,operator操作符,可以实现将已经定义的操作符重新定义,完成对自定义类型对象的运算功能
  • 双目操作符L#R

操作符重载注意事项

  • 如果一个操作符的所有操作数都是基本类型则无法重载
  • 操作符重载不会给变编译器预定义的优先级
  • 操作符重载不能改变操作数的个数
  • 无法通过操作符重载机制发明新的符号
  • 只能使用成员函数形式的操作符
    = [] ( ) ->

不可以重载的操作符

  • 作用域限定操作符::
  • 直接成员访问操作符.
  • 直接成员指针解引用操作符.*
  • 条件操作符a>b?a:b
  • 字节长度操作符sizeof
  • 类型信息操作符typeid

双目操作符

计算类双目操作符+ - * /

  • 返回值类型
    表达式结果是右值,不能对表达式结果再次赋值,返回值因此是常对象
  • 参数
    右操作数既可以是左值也可以是右值,因此使用常引用
    左操作数既可以是左值也可以是右值,因此使用常函数(修饰this指针)
  • 实现
    成员函数形式(左调右参) 常函数
    L#R
    可以被编译器处理成L.operator#(R)的成员函数调用形式,该函数的返回值就是表达式的结果
    全局函数形式(左右都参) 友元函数
    L#R
    可以被编译器处理成operator#(L,R)的全局函数调用形式,该函数的返回值就是表达式结果
  • 注意
    两种形式只能使用一个
    如果使用全局函数形式,在访问类中私有成员时需要使用friend关键字在类中的私有成员将其声明为该类的友元,友元函数可以访问该类的所有成员
#include <iostream>
using namespace std;

class Complex {
public:
    Complex(int r, int i) : m_r(r), m_i(i) {}

    void print() const {
        cout << m_r << "+" << m_i << "i" << endl;
    }

    //成员函数调用形式
    const Complex operator+(const Complex &other) const {
        Complex tmp(this->m_r + other.m_r, this->m_i + other.m_i);
        return tmp;
    }

private:
    int m_r, m_i;

    friend const Complex operator-(const Complex &l, const Complex &r);
};

//全局函数调用形式,在类中声明成友元函数
const Complex operator-(const Complex &l, const Complex &r) {
    Complex tmp((l.m_r - r.m_r), (l.m_i - r.m_i));
    return tmp;
}

int main() {
    Complex a1(3, 7);
    Complex a2(2, 3);
    a1.print();//3+7i
    a2.print();//2+3i
    //等同于Complex a3 = a1.operator+(a2);成员函数调用形式
    Complex a3 = a1 + a2;
    a3.print();//5+10i
    //等同于Complex a4 = operator-(a1,a2);全局函数调用形式
    Complex a4 = a1 - a2;
    a4.print();//1+4i
    return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
3+7i
2+3i
5+10i
1+4i

进程已结束,退出代码 0

赋值类双目操作符+= -= ...

  • 返回值类型
    表达式结果是左值并且是左操作数自身,表达式结果可以再次赋值;因此返回的是非常对象引用
  • 参数
    左操作数必须是左值,而右操作数既可以是左值也可以是右值;因此左参数是非常引用,右参数是常引用
  • 实现
    成员函数形式(左调右参) 非常函数
    L#R
    可以被编译器处理成 L.operator#(R)的成员函数调用形式,该函数的返回值就是左操作数本身return *this;
    全局函数形式(左右都参) 非常函数
    L#R
    可以被编译器处理成operator(L.R)的全局函数调用形式,该函数的返回值就是左操作数本身 return L;
  • 注意
    两种形式只能使用一个
    如果使用全局函数形式,在访问类中私有成员时,需要使用friend关键字在类中的私有成员里,将其声明为友元,友元函数可以访问该类的所有成员
#include <iostream>

using namespace std;

class Complex {
public:
    Complex(int r, int i) : m_r(r), m_i(i) {}

    void print() const {
        cout << m_r << "+" << m_i << "i" << endl;
    }

    //成员函数调用形式
    Complex &operator+=(const Complex &r) {
        this->m_r += r.m_r;
        this->m_i += r.m_i;
        return *this;
    }

private:
    int m_r, m_i;

    friend Complex &operator-=(Complex &l, const Complex &r);

};

//全局函数调用形式,在类中声明成友元函数
Complex &operator-=(Complex &l, const Complex &r) {
    l.m_r -= r.m_r;
    l.m_i -= r.m_i;
    return l;
}

int main() {
    Complex a1(3, 7);
    Complex a2(2, 3);
    a1.print();//3+7i
    a2.print();//2+3i
    //等同于Complex a2 = a2.operator+=(a1);成员函数调用形式
    a2 += a1;
    a2.print();//5+10i
    Complex a3(7, 9);
    (a1 += a2) = a3;
    a1.print();//7+9i

    //等同于Complex a1 = operator-=(a1,a2);
    a1 -= a2;
    a1.print();//2+-1i
    (a3 -= a1) = a2;
    a3.print();//5+10i
    return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
3+7i
2+3i
5+10i
7+9i
2+-1i
5+10i

进程已结束,退出代码 0

单目操作符重载 #O

计算类单目操作符 - ~ ...

  • 返回值类型
    表达式结果是是右值,不能对表达式结果再次赋值;因此返回的对象时常对象
  • 参数
    操作数自身可以是左值.也可以是右值;因此参数是常引用
  • 实现
    成员函数 常函数
    #O
    单目操作符重载可以被编译器处理成O.operator#()的成员函数调用形式,该函数的返回值就是表达式的结果,是一个临时变量
    全局函数形式 常函数
    #O
    可以被编译器处理成operator#(O)的全局函数调用形式,该函数的返回值就是表达式的结果,是一个临时对象
  • 注意
    两种形式只能使用一个
    如果使用全局函数形式,在访问类中私有成员时,需要使用friend关键字在类中的私有成员;里,将其声明为该类友元,友元函数可以访问该类的所有成员
#include <iostream>

using namespace std;

class Integer {
public:
    explicit Integer(int i) : m_i(i) {}

    void print() const {
        cout << m_i << endl;
    }

    //成员函数调用
    const Integer operator-(void) const {
        Integer tmp(-m_i);
        return tmp;
    }

private:
    int m_i;
//全局函数调用
    friend const Integer operator~(const Integer &l);
};

//全局函数调用
const Integer operator~(const Integer &l) {
    Integer tmp(l.m_i * l.m_i);
    return tmp;
}


int main() {
    Integer i(100);
    i.print();//100
    Integer j(-i);
    j.print();//-100

    j = (~i);
    j.print();//10000
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
100
-100
10000

进程已结束,退出代码 0

自增减类单目操作符++ --

++i --i
  • 返回值类型
    表达式结果是左值,而且返回的是就是操作数自身,因此返回值类型是左值引用
  • 参数
    操作数必须是左值,因此参数必须是左值引用
  • 实现
    成员函数形式为非常函数
    #O
    可以被编译器处理成O.operator#()的成员函数调用形式,该函数的返回值就是操作数本身
    全局函数形式为非常函数
    可以被编译器处理成operator#(O)的全局函数调用形式,该函数的返回值就是操作符本身
i++ i--
  • 返回值类型
    表达式结果是右值,右值,并且是操作数之前的数值,是一个临时变量
  • 参数
    操作数必须是左值
  • 实现
    成员函数形式O#(int)
    可以被编译器处理成O.operator#(int/*哑元参数*/)的成员函数调用形式,该函数的返回值就是一个临时变量
    全局函数形式#(O,int)
    可以被编译器处理成operator#(O,int/*哑元参数*/)的成员函数调用形式,该函数的返回值就是一个临时变量
#include <iostream>

using namespace std;

class Integer {
public:
    explicit Integer(int i) : m_i(i) {}

    void print() const {
        cout << m_i << endl;
    }

    //成员函数调用a++
    const Integer operator++(int/*哑元*/) {
        Integer tmp = *this;
        ++m_i;
        return tmp;
    }

    //成员函数调用++a;
    Integer &operator++() {
        ++m_i;
        return *this;
    }

private:
    int m_i;

    //全局函数调用a++
    friend const Integer operator--(Integer &l, int/*哑元*/);

    //全局函数调用--a
    friend Integer &operator--(Integer &l);
};

//全局函数调用a++
const Integer operator--(Integer &l, int/*哑元*/) {
    Integer tmp = l;
    --l.m_i;
    return tmp;
}

//全局函数调用--a
Integer &operator--(Integer &l) {
    --l.m_i;
    return l;
}

int main() {
    Integer i(100);
    i.print();//100
    Integer j = i++;
    j.print();//100
    i.print();//101
    j = i--;
    j.print();//101
    i.print();//100
    j = ++i;
    j.print();//101
    i.print();//101
    j = --i;
    j.print();//100
    i.print();//100
    j = ++ ++i;
    j.print();//102
    i.print();//102
    j = -- --i;
    j.print();//100
    i.print();//100
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
100
100
101
101
100
101
101
100
100
102
102
100
100

进程已结束,退出代码 0

输入输出操作符 << >>

  • 功能
    实现自定义类型对象的直接输出或输入

  • 形式
    成员函数调用形式 operator>>(O);
    全局函数调用形式 operator<<(cout,O);

  • 注意
    cout cin对应的标准输出和输入流类是C++标准库定义好的,要想添加新的成员函数很麻烦,所以使用全局成员函数的形式

  • 全局函数调用实现

//将r中的数据读取到输出缓冲区中,r可以是左值也可以是右值
friend ostream& operator<<(ostream& os,const Right& r){
	return os;
}
//把输入缓冲区的内容读入到参数r中,r只能是左值
friend istream& operator>>(istream& is,Right& r){
	return is;
}
  • 代码
#include <iostream>

using namespace std;

class Complex {
public:
    Complex(int r, int i) : m_r(r), m_i(i) {}

private:
    int m_r, m_i;

    friend ostream &operator<<(ostream &os, const Complex &r);

    friend istream &operator>>(istream &is, Complex &a);
};

ostream &operator<<(ostream &os, const Complex &r) {
    os << r.m_r << "+" << r.m_i << "i" << endl;
    return os;
}

istream &operator>>(istream &is, Complex &a) {
    int r, i;
    is >> m_r >> m_i;
    return is;
}

//全局函数调用形式,在类中声明成友元函数

int main() {
    Complex a1(3, 7);
    Complex a2(2, 3);
    cout << a1 << endl;
    cin >> a2;
    cout << a1 << "," << a2 << endl;
    return 0;
}
#include <iostream>

using namespace std;

class T {
public:
    T() {
        for (int i = 0, count = 1; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                m_t[i][j] = count++;
            }
        }
    }

    const T operator+(const T &other) const {
        T tmp;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                tmp.m_t[i][j] = this->m_t[i][j] + other.m_t[i][j];
            }
        }
        return tmp;

    }
    
private:


    int m_t[3][3];


    friend ostream &operator<<(ostream &os, const T &t);
};

ostream &operator<<(ostream &os, const T &t) {
    for(int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            cout << t.m_t[i][j] << " ";
        }
        cout << endl;
    }
    return os;
}

//全局函数调用形式,在类中声明成友元函数

int main() {
    T t1;
    T t2;
    T t3 = t1 + t2;
    cout << t3;
    return 0;
}
/*    1   2   3
 *  1 a   b   c
 *  2 a   b   c
 *  3 a   b   c
 */
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
2 4 6 
8 10 12 
14 16 18 

进程已结束,退出代码 0

下标访问操作符 [ ]

  • 功能实现让自定义类型的对象能够像数组一样去使用
  • 注意非常对象返回左值,常对象返回右值
  • 实现
#include <iostream>

using namespace std;

class T {
public:
    T(std::size_t size) {
        m_arr = new int[size];
    }

    ~T() {
        delete[] m_arr;
    }

    int &operator[](std::size_t i) {
        return m_arr[i];
    }

    const int &operator[](std::size_t i) const {
       // return m_arr[i];
       return const_cast<T&>(*this)[i];//复用代码
    }

private:
    int *m_arr;
};

int main() {
    T t1(10);
    t1[1] = 1;
    t1[3] = 2;
    cout << t1[1] << t1[3];
/*    const T t2(3);//重载常函数版本,报错
    t2[1] = 111;
    t2[3] = 222;
    cout << t2[1] << t2[3];*/
/*
/home/panda/WorkSpace/memptr/main.cpp: In function ‘int main()’:
/home/panda/WorkSpace/memptr/main.cpp:33:11: error: assignment of read-only location ‘t2.T::operator[](1)’
   33 |     t2[1] = 111;
/home/panda/WorkSpace/memptr/main.cpp:34:11: error: assignment of read-only location ‘t2.T::operator[](3)’
   34 |     t2[3] = 222;
   */
    return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
12
进程已结束,退出代码 0

函数操作符()

  • 功能实现让自定义类型的对象能够像函数一样去使用,这样的对象被称为仿函数
  • 注意对于返回结果,参数个数,参数类型没有任何限制
#include <iostream>

using namespace std;

class Func {
public:
    int operator()(int i, int j) {
        return i * j;
    }

    int operator()(int i) {
        return i * i;
    }

private:

};

int main() {
    Func f;
    cout << f(3, 2)<<endl;
    cout << f(4);

    return 0;
}

new delete操作符

  • 对象动态创建和销毁过程
  1. 调用operator new分配对象所需要的内存编译器实现的operator new
  2. 调用类中的构造函数
  3. 调用类的析构函数
  4. 调用operator delete编译器实现的operator delete
  • 注意
    在对象创建,内存分配之前还没有对象可以使用,所以要是用静态成员对象
  • 形式
    static void* operator new(size_t size){...}
    static void operator delete(void* pv){...}
#include <iostream>

using namespace std;

class A {
public:
    A(){
        cout<<"create A"<<endl;
    }
    ~A(){
        cout <<"delete A"<<endl;
    }
    static void* operator new(size_t size){
        cout<<"malloc A"<<endl;
        void* pv = malloc(size);
    }
    static  void operator delete (void* pv){
        cout <<"free A"<<endl;
        free(pv);
    }
private:

};

int main() {

    //1>先分配内存
    // A* pa = A*A::operator new(sizeof(A));
    //2>在调用构造函数
    // pa->构造函数
    A* pa =new A;
    delete pa;
    return 0;
}
/home/panda/WorkSpace/memptr/cmake-build-debug/memptr
malloc A
create A
delete A
free A

进程已结束,退出代码 0

操作符重载速查表

双目操作符表达式结果右操作数左操作数(函数体影响左操作数的this指针)
计算类双目操作符右值可左可右可左可右
成员函数调用返回值类型const常引用常函数
全局函数调用返回值类型const常引用常引用
赋值类双目操作符左值,并且是左操作数自身可左可右必须左值
成员函数调用cosnt修饰return *this;常引用普通函数
全局函数调用cosnt修饰return 左操作数;常引用左值引用
单目操作符表达式结果操作数
计算类双目操作符右值可左可右
成员函数调用cosnt修饰常函数
全局函数调用cosnt修饰常引用
自增自减单目操作符--
a++; a--; /*哑元参数int*/右值,并且是操作数之前的数值必须是左值
成员函数调用cosnt修饰返回值return中间变量;普通函数
全局函数调用cosnt修饰返回值return中间变量;左值引用
++a; --a;左值,并且是左操作数自身必须是左值
成员函数调用加引用,return *this;
普通函数
全局函数调用加引用,return 操作数;左值引用

速查表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值