IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 基础与深度分析 Chapter12 类的细节(运算符重载) -> 正文阅读

[C++知识库]C++ 基础与深度分析 Chapter12 类的细节(运算符重载)

使用operator关键字引入重载函数

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Str
{
    int val = 3;
};

struct Str2
{
};

Str Add(Str x, Str y)
{
    Str z;
    z.val = x.val + y.val;
    return z;
}

auto operator + (Str x, Str y)
{
    Str z;
    z.val = x.val + y.val;
    return z;
}

auto operator + (Str2 x, Str2 y) // 函数名称相同,参数类型不同,本质是函数重载
{
    
}

int main()
{
    Str x;
    Str y;
    // Str z = Add(x, y); // x + y 更简洁
    Str z = x + y; // 运算符重载
    cout << z.val << endl;
}

重载不能发明新的运算,重载的一定是已经存在的运算。
不能改变运算的优先级与结合性。虽然我们可以定义运算的具体行为,但是不改变优先级和结合性。
通常不改变运算含义,比如本来是加号,不要内部改为-的含义。这样对使用者不友好。

函数参数个数与运算操作数个数相同,至少一个为类类型
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
除 operator() 外其它运算符不能有缺省参数,因为()里面对于个数没有要求。
在这里插入图片描述
在这里插入图片描述
可以选择实现为成员函数与非成员函数
通常来说,实现为成员函数会以 *this 作为第一个操作数(注意 == 与 <=> 的重载)

#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Str
{
    int val = 3;

    auto operator () (int y = 100) // 可以是缺省参数
    {
        return val + y;
    }

    auto operator + (Str x) // 
    {
        Str res;
        res.val = val + x.val;
        return res;
    }
};


int main()
{
    Str x;
    cout << x(5) << endl; // 8
    Str a;
    Str b;
    b.val = 10;
    Str c = a + b; // 成员函数+重载,a对应缺省的this*, b对应x
    cout << c.val << endl; // 13
}

运算符的进一步划分

根据重载特性,可以将运算符进一步划分
在这里插入图片描述
上面的运算符都是可以重载的。

  • 可重载且必须实现为成员函数的运算符( =,[],(),-> 与转型运算符)
  • 可重载且可以实现为非成员函数的运算符
  • 可重载但不建议重载的运算符( &&, ||, 逗号运算符)C++17 中规定了相应的求值顺序但没有方式实现短路逻辑
    在这里插入图片描述
  • 不可重载的运算符(如 ? :运算符)

运算符重载——详述1

在这里插入图片描述
对称运算符通常定义为非成员函数以支持首个操作数的类型转换

#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Str
{
    Str(int x)
        : val(x)
    {}

   /* auto operator + (Str input)
    {
        return Str(val + input.val);
    } */
    int val;
};

// 这样就可以考虑 4+x 了
auto operator + (Str input1, Str input2)
{
    return Str(input1.val + input2.val);
}


int main()
{
    Str x = 3;
    Str y = 4;
    Str z1 = x + y; // 可以
    Str z2 = x + 4; // 可以
    Str z3 = 4 + x; // 不可以,因为不会调用重载的成员+函数,因为4不是Str类型,和this不匹配
    // 这就是为什么要把对称的操作符重载改成非成员函数

    // cout << z.val << endl;
}

在这里插入图片描述
移位运算符一定要定义为非成员函数,因为其首个操作数类型为流类型
在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Str
{
    Str(int x)
        : val(x)
    {}

    friend auto& operator << (std::ostream& ostr, Str input)
    {
        ostr << input.val;
        return ostr;
    }
private:
    int val;
};

int main()
{
    Str x = 3;
    Str y = 4;
    cout << x << y;
}

赋值运算符也可以接收一般参数

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int x)
        : val(x)
    {}

    // 拷贝赋值运算符
    Str& operator = (const Str& input)
    {
        val = input.val;
        return *this;
    }

    // 赋值运算符也可以接收一般参数
    Str& operator = (const std::string input)
    {
        val = static_cast<int>(input.size());
        return *this;
    }

    friend auto& operator << (std::ostream& ostr, Str input)
    {
        ostr << input.val;
        return ostr;
    }
    int val;
};

int main()
{
    Str x = 3;
    x = "12345"; // 赋值运算符
    cout << x.val;
}

operator [] 通常返回引用,来模拟数组的访问

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int x)
        : val(x)
    {}

    // 拷贝赋值运算符
    Str& operator = (const Str& input)
    {
        val = input.val;
        return *this;
    }

    // 赋值运算符也可以接收一般参数
    Str& operator = (const std::string input)
    {
        val = static_cast<int>(input.size());
        return *this;
    }

    friend auto& operator << (std::ostream& ostr, Str input)
    {
        ostr << input.val;
        return ostr;
    }
    // 只有这里返回引用,[]这个操作符才能有类似数组写的操作
    int& operator[] (int id)
    {
        return val;
    }

    // const [] 的重载,不要返回引用,因为const不让他修改
    int operator[] (int id) const
    {
        return val;
    }

    int val;
};


int main()
{
    Str x = 3;
    x = "12345"; // 赋值运算符
    cout << x[0] << endl; // 5
    x[0] = 100; // 因为是返回int&,所以才能写操作
    cout << x[0] << endl; // 100 
    const Str cx = 11;
    cout << cx[0] << endl; // 11 调用的重载的[],带const的。如果x是非const,就调用非const的
}

自增、自减运算符的前缀、后缀重载方法
怎么来判断是前缀自增++x或者x++后缀自增呢?
在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int x)
        :val(x)
    {}

     // 拷贝赋值运算符
    Str& operator = (const Str& input)
    {
        val = input.val;
        return *this;
    }

    // 赋值运算符也可以接收一般参数
    Str& operator = (const std::string input)
    {
        val = static_cast<int>(input.size());
        return *this;
    }

    //  前缀自增
    Str& operator++ ()
    {
        val += 100;
        return *this;
    }

    //  后缀自增
    Str operator++ (int)
    {
        Str tmp(*this); // 拷贝构造副本
        val += 10;
        return tmp;
    }
    int val;
};


int main()
{
    Str s(3);
    cout << (++s).val << endl; // 103
    int y = s++.val;
    cout << y << endl; // 103 s的值先赋予y,再加10
    cout << s.val << endl; // 113

}

能使用前缀自增自减,就使用前缀,不使用后缀的
在这里插入图片描述
使用解引用运算符( * )与成员访问运算符( -> )模拟指针行为
.运算符一般是用于成员对象的对象.x,对象.fun();
->一般是用于指针的,对象指针->x, 对象指针->fun();
.不能重载,->可以重载

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int* p)
        : ptr(p)
    {

    }
    // 参数为空,隐式参数Str,一个就够了
    // 这里为什么要写int型引用呢?因为这样可以对*ptr进行写操作
    int& operator * ()
    {
        return *ptr;
    }

    // ->成员访问操作符的重载
    Str* operator -> ()
    {
        return this;
    }
    
    int val = 5;

private:
    int* ptr;
};


int main()
{
    int x = 100;
    Str ptr(&x);
    cout << ptr->val << endl;
}

使用函数调用运算符构造可调用对象,接收参数个数不定

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int p)
        : val(p)
    {}

    // 第一个括号是函数调用运算符的重载,第二个是放参数
    int operator() ()
    {
        return val;
    }

    int operator() (int x, int y, int z)
    {
        return val + x + y + z;
    }
private:
    int val;
};


int main()
{
    Str obj(100);
    // obj()很想一个函数调用,但obj本质是一个对象,可以使用函数调用的方式调用obj
    // 这样做的好处:没有lambada之前,可以构造函数的可调用对象
    
    cout << obj() << endl; // 100
    cout << obj(1, 2, 3) << endl; // 106 合法的函数调用
}

运算符重载——详述2

类型转换运算符

在这里插入图片描述
类型转换运算符,把一种类型转换成另外一种类型。

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    Str(int p)
        : val(p)
    {}
    
    // 函数声明 没有显式的返回类型,返回类型已经在类型的名称那里体现了,int类型
    // 对象本身不改变,加const
    operator int() const
    {
        return val;
    }
private:
    int val;
};


int main()
{
    Str obj(100);
    int v = obj; // 对int变量的构造,等号右边也是int类型才行
    cout << v << endl; 
}

与单参数构造函数一样,都引入了一种类型转换方式

注意避免引入歧义性与意料之外的行为
通过 explicit 引入显式类型转换

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    explicit Str(int p)
        : val(p)
    {}
    
    // 如果这里加explicit,那么obj+3就不调用这个,因为obj无法隐式转换
    operator int() const
    {
        return val;
    }

    
    friend auto operator + (Str a, Str b)
    {
        return Str(a.val + b.val);
    }
private:
    int val;
};


int main()
{
    Str obj(100); 
    // 3不能隐式转换,所以走不了3转成Str,只能走obj转int
    obj + 3; // 这里就会有歧义,不知道是ob转成了int与3相加,还是3转成obj,做+重载
}

在这里插入图片描述
explicit bool 的特殊性:用于条件表达式时会进行隐式类型转换

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    explicit Str(int p)
        : val(p)
    {}
    
    explicit operator bool() const
    {
        return (val == 0);
    }

private:
    int val;
};


int main()
{
    Str obj(100); 
    
    auto var = obj ? 1 : 0;
    cout << var << endl;
    if (obj) // 如果explicit返回的是bool值,并且对象明显用于条件表达式,那么可以隐式转换
    {
        cout << 1 << endl;
    }
    else
    {
        cout << 0 << endl;
    }
}

在这里插入图片描述

C++ 20 中对 == 与 <=> 的重载

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <string>
using namespace std;

struct Str
{
    explicit Str(int p)
        : val(p)
    {}
    
    friend bool operator == (Str obj, Str obj2)
    {
        return obj.val == obj2.val;
    }

private:
    int val;
};


int main()
{
    Str obj(100);
    Str obj2(100);
    cout << (obj == obj2) << endl;
    cout << (obj != obj2) << endl; // c++20,只要定义了==,编译器可以自动推导!=,其他标准不行
}

在这里插入图片描述
在这里插入图片描述
注意 <=> 可返回的类型: strong_ordering, week_ordering, partial_ordering

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 22:37:15  更:2022-07-04 22:40:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 12:30:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码