<<从0到1学C++>> 第8篇 运算符重载

本篇要学习的内容和知识结构概览


运算符重载使用场景

常规赋值操做ios

// = 赋值运算符 普通的赋值操做
int a, b;
a = 5;
b = a;复制代码

咱们如今有一个类数组

class str {
    char * st;
    
public:
    // 使用指针的构造函数
    str(char * s) {
        st = new char[strlen(s) + 1];
        strcpy(st, s);
    }
};复制代码

想要实现这种赋值操做bash

str s1("abc");
str s2("def");
s1 = s2;复制代码

就须要咱们经过重载"="赋值运算符来实现了函数

具体实现以下
学习

#include <iostream>
using namespace std;

class str {
    char * st;
    
public:
    // 使用指针的构造函数
    str(char * s) {
        st = new char[strlen(s) + 1];
        strcpy(st, s);
    }
    
    // 使用引用的构造函数
    str(str & a) {
        st = new char[strlen(a.st) + 1];
        strcpy(st, a.st);
    }
    
    // 重载使用对象引用的赋值运算符
    str & operator=(str & a) {
        if (this == &a) {
            return *this;
        }
        delete st;
        st = new char(strlen(a.st) + 1);
        strcpy(st, a.st);
        return *this;
    }
    
    // 输出字符串
    void print() {
        cout << st << endl;
    }
    
    // 析构函数
    ~str() {
        delete st;
    }
};

int main() {
    
    // 两个字符数组
    char c1[] = "hello";
    char c2[] = "world";
    
    // 三个对象
    str s1(c1);
    str s2(c2);
    str s3(s1);
    
    // 如今咱们能够实现表象上的 = 赋值操做
    s3 = s2 = s1;

    // 等价于下面这句话
    s3.operator=(s2.operator=(s1));

    // 等价于下面这两句话
    s2.operator=(s1);
    s3.operator=(s2);
    
    // 打印
    s1.print();
    s2.print();
    s3.print();
}复制代码

因此说呢: ui

咱们在使用运算符进行运算的时候, 实际上也是经过函数来实现运算的.this

任何运算都是经过函数来实现的, 因此经过运算符来进行计算, 实际也是经过函数来完成spa

运算符重载的实质

表达式 7 / 2 = 3, 7.0 / 2.0 = 3.5, 同一个运算符 / , 具备不一样的意义, 称之为”运算符重载”, 实际上就是"函数重载".设计

每一个运算符都有本身的函数形式, 像下面这些指针

7 + 2 的函数形式就是 operator + (7, 2)

7 - 2 的函数形式就是 operator - (7, 2)

7 * 2 的函数形式就是 operator * (7, 2)

7 / 2 的函数形式就是 operator / (7, 2)

因此, 要重载某个运算符, 只要重载相应的函数就能够了

好比: 

// string对象进行 + 运算
string a = "abc";
string b = "def";
cout << a + b << endl;复制代码

定义的重载运算符都要求可以访问这个类型的私有成员, 在这个前提下:

要么将运算符重载为这个类型的成员函数

要么将运算符重载为这个类型的友元

将做为类的成员函数的重载运算符称为类运算符

将做为类的友元重载运算符称为友元运算符

可重载运算符和不可重载运算符的列表

下面是: 可重载运算符与只能用类运算符重载和只能用友元运算符重载和不是运算符的区分


插入符<< 和 提取符 >> 的重载

注意: 

<< 或者 >> 输入流和输出流都是标准类库, 不可修改, 因此不能在它们本身的类里重载为类运算符

操做符的左边是流对象, 而不是被操做的对象,并且咱们还要访问被操做对象的私有数据, 因此咱们只能将它们做为被操做类对象的友元重载

#include <iostream>
using namespace std;

// 一个新的类
class Test {
    // 三个成员属性
    int i;
    float f;
    char c;
    
public:
    
    // 构造函数
    Test(int a = 0, float b = 0, char ch = '\0') {
        i = a;
        f = b;
        c = ch;
    }
    
    // 友元重构
    friend ostream & operator << (ostream &, Test &);
    
    // 友元重构
    friend istream & operator >> (istream &, Test &);
};

// 重载插入符
ostream & operator << (ostream & output, Test & test) {
    output << test.i << ", ";
    output << test.f << ", ";
    output << test.c << endl;
    return output;
}

// 重载提取符
istream & operator >> (istream & input, Test & test) {
    input >> test.i;
    input >> test.f;
    input >> test.c;
    return input;
}

int main() {
    
    // 经过重载提取符实例一个对象
    Test a, b;
    
    // 能够这么写 函数调用形式
//    operator >> (cin, a);
//    operator >> (cin, b);
    
//    operator << (cout, a);
//    operator << (cout, b);
    
    // 也能够这么写 运算符形式
    cin >> a >> b;
    // << 插入符之因此能够连续使用是由于 cout << a 这个表达式返回cout对象, 继续输出b对象 cout << b
//    cout << a << b << endl;
//    operator <<(operator << (cout, a), b);

}复制代码
注意: 不能本身定义新的运算符, 只能是把原有的运算符用到本身设计的类上去

++ 运算符的重载:

做为类运算符的重载

#include <iostream>
using namespace std;

class Number {
    int num;
    
public:
    Number(int i) {
        num = i;
    }
    
    // ++在前 编译器默认: 重写++时, 没有参数, 为++在前
    int operator ++ () {
        num++;
        return num;
    }
    
    // ++ 在后: 编译器默认: 重写++时, 有一个参数, 为++在后
    int operator ++ (int) {
        int i = num;
        num ++;
        return i;
    }
    
    void print() {
        cout << num << endl;
    }
};

int main() {
    Number n(10);
//    int a = ++n;
    int a = n.operator++();
    cout << a << endl;
    n.print();
    
//    a = n++;
    a = n.operator++(0);
    cout << a << endl;
    n.print();

}复制代码

做为友元运算符的重载

#include <iostream>
using namespace std;

class Number {
    int num;
    
public:
    Number(int i) {
        num = i;
    }
    
    // ++ 在前: 一个参数 ++ 在前
    friend int operator ++ (Number &);
    
    // ++ 在后: 两个参数 ++ 在后
    friend int operator ++ (Number &, int);
    
    void print() {
        cout << num << endl;
    }
};

int operator ++ (Number & a)  {
    a.num++;
    return a.num;
}

int operator ++ (Number & a, int)  {
    int i = a.num;
    a.num ++;
    return i;
}

int main() {
    Number n(10);
//    int i = ++n; 这个句子会调用operator++(Number)这个函数, 也就是++在前
    int i = operator++(n);
    cout << i << endl;
    n.print();
    
//    i = n++; 这个句子会调用operator++(Number, int)这个函数, 也就是++在后
    i = operator++(n, 0);
    cout << i << endl;
    n.print();
}复制代码
注意:

通过重载, 运算符并不改变原有的优先级, 也不改变所需操做数目

当不涉及到定义的类对象时, 它仍然执行系统预约义的运算, 只有用到本身定义的对象止, 才执行新定义的操做

类运算符和友元运算符的区别

若是运算符所需的操做数但愿进行隐式类型转换, 则运算符应经过友元来重载

若是一个运算符的操做须要修改类对象的状态, 则应当使用类运算符

运算符 + 做为友元运算符

#include <iostream>
using namespace std;

class complex {
    double real, imag;
    
public:
    complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }
    
    // 友元重载
    friend complex operator + (complex, complex);
    
    void show() {
        cout << real << "+" << imag << "i" << endl;
    }
};

// 重载运算符
complex operator + (complex a, complex b) {
    double r = a.real + b.real;
    double i = a.imag + b.imag;
    return complex(r, i);
}

int main() {
    complex x(3, 2), y;
    /**
     friend complex operator + (complex &, complex &);
     若是将友元运算符声明成这样: 则下面两个语句都编译错误
     */
    // 使用友元运算符咱们就能够将 7 隐式类型转成 7 + 0i 而后再进行 + 运算
    y = x + 7; // 等价于 y = operator(x, 7)
    y = 7 + y; // 等价于 y = operator(7, y);
    y.show();
}复制代码

运算符 + 做为类运算符 (会出现编译错误)

#include <iostream>
using namespace std;

class complex {
    double real, imag;
    
public:
    complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }
    
    // 类重载
    complex operator + (complex a) {
        double r = a.real + real;
        double i = a.imag + imag;
        return complex(r, i);
    }
    
    void show() {
        cout << real << "+" << imag << "i" << endl;
    }
};

int main() {
    complex x(3, 2), y;
    /**
     complex operator + (complex, complex);
     若是将类运算符声明成这样: y = 7 + y; 语句编译错误
     */
    y = x + 7; // 等价于 y = x.operator(7)
//    y = 7 + y; // 等价于 y = 7.operator(y);
    y.show();
}复制代码
注意:

在上面的main函数代码中, 若是对象做为重载运算符函数的参数, 则可使用构造函数将常量转换成该类型的对象. 若是使用引用做为参数, 这些常量不能做为对象名使用, 因此编译错误

总结

在学习C++这门语言的时候明显的感受到她的一应俱全, 丰富多彩. 她有本身的不少特性, 表如今使用上就是更加的灵活, 总得来讲就是: 没有她没有的, 只有你想不到的! 我会继续深刻学习C++, 继续挖掘语言的本质!

本系列文章会持续更新! 你们踊跃的留下本身的脚印吧!

👣👣👣👣👣👣👣👣

相关文章
相关标签/搜索