• 【c ++ primer 笔记】第 14章 重载运算符


    在这里插入图片描述

    🎉作者简介:👓 博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢 c + + , g o , p y t h o n , 目前熟悉 c + + , g o 语言,数据库,网络编程,了解分布式等相关内容 \textcolor{orange}{博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++,go语言,数据库,网络编程,了解分布式等相关内容} 博主在读机器人研究生,目前研一。对计算机后端感兴趣,喜欢c++,go,python,目前熟悉c++go语言,数据库,网络编程,了解分布式等相关内容
    📃 个人主页: \textcolor{gray}{个人主页:} 个人主页: 小呆鸟_coding
    🔎 支持 : \textcolor{gray}{支持:} 支持: 如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦 \textcolor{green}{如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦} 如果觉得博主的文章还不错或者您用得到的话,可以免费的关注一下博主,如果三连收藏支持就更好啦👍 就是给予我最大的支持! \textcolor{green}{就是给予我最大的支持!} 就是给予我最大的支持!🎁
    💛本文摘要💛

    本专栏主要是对c++ primer这本圣经的总结,以及每章的相关笔记。目前正在复习这本书。同时希望能够帮助大家一起,学完这本书。 本文主要讲解第14章 重载运算符


    c++ primer 第五版 系列文章:可面试可复习

    第2章 变量和基本类型
    第3章 字符串、向量和数组
    第4章 表达式
    第5章 语句
    第6章 函数
    第8章 IO库
    第9章 顺序容器
    第10章 泛型算法
    第11章 关联容器
    第12章 动态内存
    第13章 拷贝控制
    第 14章 重载运算符
    第15章 面向对象程序设计
    第 16章 模板与泛型编程

    重载运算符

    😪14.1 基本概念

    • 重载的运算符是有特殊名字的函数:名字由关键字 operator 和要定义的运算符号组成
    • 重载的运算符作为函数,也包含返回类型参数列表函数体
    • 如果运算符函数是类的成员函数,它第一个(左侧)运算对象绑定到隐式的 this 指针上,因此定义成员运算符函数时的参数数量比运算符的运算对象少一个。
    • 重载的运算符必须是某个类的成员或至少拥有一个类类型的运算对象。
    • 除了重载的函数调用运算符 operator() 之外,其他重载运算符不能含有默认实参。

    错误的做法

    • 运算符作用于内置类型的运算对象时,无法改变该运算符的含义(不能改变优先级和结合律,要和内置运算符保持一致)
    '错误:不能为int重定义内置的运算符'
    int operator+(int, int)
    
    • 1
    • 2
    • 只能重载已有的运算符,不能发明新的运算符

    重载的俩种方法

    • 直接调用:如 data1+data2;data1+=data2。+ 是非成员函数,+= 是类的成员函数,两种都可以直接使用。
    • 函数调用:如 operator+(data1,data2);data1.operator+=(data2)。

    重载运算符的规则

    1. 通常,不应该重载逗号,取地址,逻辑与和逻辑或运算符。
    2. 重载的运算符应该使用和内置类型一样的含义。
    3. 如果定义了 ==,一般也应该定义 !=
    4. 如果定义了 <,也应该有其他关系操作。
    5. 如果定义了算术运算符或位运算符,也应该有对应的复合赋值运算符,如有 +,也应该有 +=。
    6. 重载运算符的返回类型应该与内置类型的返回类型兼容。如逻辑和关系运算符返回 bool,赋值和复合赋值运算符返回左侧运算对象的一个引用
    7. 当运算符定义为成员函数,它的左侧运算对象必须是所属类的一个对象。

    运算符应该定义成成员还是非成员的规则

    1. 赋值、下标([ ])、调用(( ))、成员访问箭头(->)运算符都必须是成员。
    2. 复合赋值运算符一般应该是成员,但非必须。
    3. 递增、递减和解引用等运算符应该是成员。
    4. IO 运算符应该是非成员,但是应该声明为类的友元。
    5. 具有对称性的运算符可能转换任意一端的运算对象,如算术、相等性、关系和位运算符等,应该是非成员。
    string s = "world";
    string t = s + '!';  //正确,等价于s.operator("!"),可以把一个cosnt char* 加到一个string对象中
    string u = "hi" + s; //如果+是string成员则错误,如果是非成员则正确
    
    '+是成员函数'
    'hi' + s等价于hi.operator+(s),而hi的类型是const char* 这是内置类型,没有成员函数
    
    '+是非成员函数'
    "hi" + s等价于operator+("hi", s)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    😪14.2 输入和输出运算符

    😡14.2.1 重载输出运算符<<

    • 通常输出运算符的第一个形参是一个非常量 ostream 对象的引用(因为 IO 类型不能拷贝,所以必须是引用,因为要通过向流写入来输出,这会改变流的状态,所以必须是非常量),第二个形参一般是常量引用(为了避免复制实参,所以应为引用)。重载的 << 应该返回它的 ostream 形参。

    例子

    ostream &operator<<(ostream &os, const Sales_data &item)
    {
    	os << item.isbn() << item.price << item.revenue;
    	return os;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • IO 运算符应该是非成员函数,IO运算符通常需要读写类的非公有数据成员,所以应该声明为类的友元
    • 重载输出运算符应该尽量减少格式化操作,只负责打印什么内容,而不控制格式。(不应该打印换行符)。

    😡14.2.2 重载输入运算符>>

    • 通常输入运算符的第一个形参是一个非常量 istream 对象的引用,第二个形参是要读入的非常量对象的引用。返回 istream 对象的引用

    例子

    istream& operator>>(istream& is,Sales_data& item)
    {
    	double price;
    	is >> item.bookNp >> item.units_sold >> price;
    	if (is)
    		item.revenue = item.units_sold * price;
    	else
    		item = Sales_data(); //错误,对象被赋予默认状态
    	return is;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输入错误

    1. 当流中含有错误的数据时可能会读取失败
    2. 当读取操作到达文件末尾时会失败
    3. 遇到输入流的其他错误时也会失败

    处理办法

    • 如果读取失败,输入运算符应该负责从错误中恢复,主要是将输入对象重置为合法状态,一般为未输入前的状态。
    • 输入运算符必须检查是否输入成功,并处理输入失败的情况,而输出运算符不需要。

    标示错误

    • 要设置流的条件状态以标示出失败的信息,通常设置failbit。而eofbit表示文件耗尽,badbit表示流被破坏

    😪14.3 算术和关系运算符

    • 算术和关系运算符通常定义为非成员函数以允许对左侧或右侧的运算对象进行转换。(如果定义成成员函数,就会出现限制,符号的左边就必须是this指针指向的对象0)
    • 算术和关系运算符的形参都应该是常量引用
    Sales_data operator+ (const Sales_data &lhs, const Sales_data &rhs)
    {
    	Sales_data sum = lhs;  //把lhs的数据成员拷贝给sum
    	sum += rhs;            //把rhs加到sum中
    	return sum;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:

    • 一般定义了算术运算符,也应该定义一个对应的复合赋值运算符。一般都是先定义复合赋值运算符,而后使用复合赋值来实现算术运算符(复合赋值运算符一般为成员,而算术为非成员)。
    • 一般将算术运算得到的值存放在局部变量中,操作完后返回该变量的副本

    😡14.3.1 相等运算符

    • 用来比较俩个对象是否相等

    使用规则

    1. 通常会比较对象的每一个数据成员,所有对应成员都相等时两个对象才相等。
    2. 定义了 ==,也应该定义 !=。一般通过 == 来实现 !=
    3. 通常相等运算符应该具有传递性。
    4. 如果某个类在逻辑上有相等性的含义,则该类应该定义operator==,这样做可以使用户更容易使用标准库算法来处理这个类。
    bool operator== (const Sales_data &lhs, const Sales_data &rhs)
    {
    	return lhs.isbn() == rhs.isbn() && lhs.revenue == rhs.revenue
    }
    
    bool operator != (const Sales_data &lhs, const Sales_data &rhs)
    {
    	return !(lhs == rhs)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    😡14.3.2 关系运算符

    • 定义了 == 的类,经常也会包含关系运算符,尤其是 <,因为关联容器和一些算法都要用到 <

    使用规则

    • 关系运算符一般应该定义顺序关系,令其与关联容器中对关键字的要求一致。
    • 如果同时定义了 == 和 !=,则关系运算符应该与其保持一致,比如如果两个对象是 != 的,那么一个对象应该 < 另一个

    😪14.4 赋值运算符

    • 赋值运算符必须定义为类的成员函数
    • 赋值运算符应该返回左侧运算对象的引用
    • 类中一般已经定义了拷贝赋值和移动赋值运算符。如果需要时也可以继续重载赋值运算符以使用别的类型作为右侧运算对象

    在这里插入图片描述

    • vector 可以用花括号元素列表做参数。
    • 赋值运算符必须先释放当前的内存空间,再重新分配。

    复合赋值运算符

    • 复合赋值运算符不必须是成员,但是一般也应该是成员其左侧运算对象绑定到隐式的 this 指针
    • 复合赋值运算符也返回左侧运算对象的引用
    '左侧运算对象绑定到隐式的this指针'
    ' *this += rhs '
    Sales_data & Sales_data::operator+=(const Sales_data &rhs)
    {
    	units_sold += rhs.units_sold;
    	revenue += rhs.revenue;
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    😪14.5 下标运算符

    • 下标运算符必须是成员函数
    • 下标运算符通常以所访问元素的引用作为返回值,这样下标可以出现在赋值运算符的任意一端。
    • 下标运算符最好同时定义两个版本:
      • 非常量版本:返回普通引用。
      • 常量版本:是类的常量成员并返回常量引用。常量版本取得的元素不能放在赋值运算符的左侧。

    在这里插入图片描述

    😪14.6 递增和递减运算符

    • 定义递增和递减运算符的类应该同时定义前置版本和后置版本。这些运算符通常被定义成类的成员
    • 前置版本返回递增或递减后的引用,后置版本返回修改前的副本(也就是拷贝)
      • 前置版本,返回的是我对象本身,所以返回的是引用
      • 后置版本,返回的和我当前指向的不是一个值,所以返回拷贝,而且创建临时对象

    在这里插入图片描述

    前置和后置的区分

    • 为了区分,可以使后置版本接受一个额外的不被使用的 int 类型的形参,当使用后置版本时,编译器为这个形参提供一个值为 0 的实参
    • 这个形参的唯一作用就是区分前置和后置。因为不会用到,所以该形参无需命名。
    • 后置版本可以通过调用前置版本来实现。
    Student& operator++();//前置版本
    Student& operator++(int);//后置版本
    
    • 1
    • 2

    在这里插入图片描述

    😪14.7 成员访问运算符

    • 箭头运算符必须是类的成员,箭头运算符一般通过调用解引用运算符来实现。解引用运算符通常也是类的成员,但不必须。
    • 重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象

    在这里插入图片描述

    对箭头运算符返回值的限定

    • 箭头访问符一定是调用某个成员

    在这里插入图片描述

    难点

    • 指针的调用是一个默认的内置调用
    • 对象的调用才是调用相应的重载版本(不管怎么重载,最终还是要调用它的内置的指针的行为(直接使用—>))

    例子

    • 指针
      在这里插入图片描述
    • 对象
      在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    😪14.8 函数调用运算符

    • 如果类重载了函数调用运算符,就可以像使用函数一样使用该类的对象 (被称为函数对象)。
    • 函数调用运算符必须是成员函数,一个类可以定义多个版本的调用运算符。不同版本的参数应有所区别。
    • 如果类定义了调用运算符,则该类的对象称作函数对象。

    理解

    • 函数对象本质上是一个对象,行为看上去像函数,使用该对象就像使用函数一样
    • 当重载了函数调用运算符,类对象的名字就相当于函数名,可以往里面传递各种实参
    struct AbsInt{
        int operator()(int val) const{  //该函数调用运算符返回一个整数的绝对值
            return val<0 ? -val : val;
        }
    };
    '应用'
    int i = 42
    AbsInt absObj;          //定义了一个对象
    int ui = absObj(i);   //使用重载的函数调用运算符
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    含有状态的函数对象

    • 函数对象类除了operator()之外,通常会有一些数据成员,这些数据成员帮助实现函数。
    struct PrintString{
    public:
        PrintString(ostream &_os = cout, char _sep = ' ') : os(os), sep(sep) {} 
        void operator()(const string &s) const { os<<s<<sep; }   
    };
    '应用'
    PrintString printString; //采用默认实参,输出到 cout 中,以空格为间隔符
    
    '等价于cout << ss << '';'
    printString(ss);  //打印 string 类对象 ss。
    
    '等价于cerr << s << '\n';'
    PrintString errors(cerr, '\n');
    errors(s);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 函数对象常作为泛型算法的实参
    for-each(vs.begin(), vs.end(), PrintString(cerr, '\n'));
    
    • 1

    😡14.8.1 lambda是函数对象

    • 编译器会将 lambda 翻译成一个未命名类的未命名对象,在 lambda 表达式产生的类中含有一个重载的函数调用运算符。
    • 默认情况下 lambda 不能修改它的捕获变量(上层函数的局部非 static 变量),lambda 产生的类中的函数调用运算符是一个 const 成员函数。
    • 当 lambda 被声明为可变的时,调用运算符就不是 const 的了。
    'lambda表达式'
    stable_sort(word.begin(), word.end(),[](const string &a,const string &b){ return a.size() < b.size();})
    
    '上面的lambda表达式类似这个类的未命名对象'
    class ShorterString{
    public:
        bool operator()(const string &s1, const string &s2) const { return s1.size() < s2.size(); }    
    }
    
    stable_sort(word.begin(), word.end(), ShortString);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    lambda及相应捕获行为的类

    • 当 lambda 通过引用捕获变量时,由程序确保 lambda 执行时所引用的对象确实存在。因此,编译器可以直接使用该引用而无须在lambda产生的类中将其存储为数据成员
    • 当 lambda 通过值捕获变量时,捕获的变量被拷贝到 lambda 中,此时产生的类中会建立对应的数据成员并创建构造函数来初始化数据成员。
    'lambda表达式'
    auto wc = find_if(word.begin(), word.end(),[sz](const string &a){ return a.size() >= sz})
    
    '用类表示lambda捕获行为'
    struct SizeComp{
    	SizeComp(size_t n):sz(n){}      //将捕获的变量初始化
    	
    	//该调用运算符返回类型、形参、函数体都与lambda一致
    	bool operator()(const string &s) const { return s.size() < sz; }   
    
    privatr:
    	size_t sz;       //必须将捕获的变量建立对应的数据成员,同时创建构造函数 
    }
    
    auto wc = find_if((word.begin(), word.end(), SizeComp(sz));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    😡14.8.2 标准库定义的函数对象

    • 标准库定义了一组表示算数运算符、关系运算符和逻辑运算符的模板类,每个类分别定义了一个执行命名操作的调用运算符。
    • 定义在头文件functional头文件中
    plus<int> intAdd; //实例化了一个可执行 int 加法的函数对象
    int sum = intAdd(10, 20); //调用 intAdd 来执行 int 加法
    
    • 1
    • 2

    标准库函数对象

    '算术'                         '关系'                          '逻辑'
    plus<Type>         '+'       equal_to<Type>       '='        logical_and<Type>    '与'
    minus<Type>        '-'       not_equal_to<Type>   '!='       logical_or<Type>     '或'
    multiplies<Type>   '*'       greater<Type>        '>'        logical_not<Type>    '非'
    divides<Type>      '/'       greater_equal<Type>  '>='
    modulus<Type>      '%'       less<Type>           '<'
    negate<Type>      '取反'     less_equal<Type>     '<='
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在算法中使用标准库函数对象

    • 表示运算符的函数对象类常用来替换算法中的默认运算符。
    //传入临时对象用于执行俩个string对象的>比较运算
    sort(svec.begin(), vec.end(), greater<string>());//使用 greater 对 svec 进行降序排列
    
    • 1
    • 2
    • 标准库规定的函数对象对于指针也适用,比如可以使用 less 来比较两个指针的大小(比较的是指针变量中的地址大小)
    • 因为关联容器使用 less 来对元素排序,如果将指针作为 set 或 map 的关键字,元素将自动按地址进行排序。

    注意函数对象其实是一个函数对象类。

    vectro<string *>nameTable;
    
    //错误:nameTable中的指针彼此没有关系,所以<将产生未定义行为
    sort(nameTable.begin(), nameTable.end(),[](string *a, string *b){return a < b;});
    
    //正确:标准库规定指针的less是定义的
    sort(nameTable.begin(), nameTable.end(),less<string *>());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    😡14.8.3 可调用对象与function

    • C++ 中的几种可调用的对象:函数、函数指针、lambda表达式、bind创建的对象、重载了调用运算符的类
    • 可调用对象和其他对对象一样,也有自己的类型。例如函数及函数指针的类型则由其返回值类型和实参决定的。

    调用形式指明调用返回的类型以及传递给调用的实参类型

    int(int, int); //是一个函数类型,它接受两个 int,返回一个 int
    
    • 1

    不同类型的可调用函数对象可能具有相同的调用形式

    int add(int i, int j) { return i + j; }       //普通函数
    
    auto mod = [](int i, int j) { return i % j }; //lambda 产生一个未命名的函数对象类,mod 是这个类的一个实例
    
    struct divide{                                //一个函数对象类
        int operator()(int i, int j) { return i / j; }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    标准库function类型

    • 定义函数表用来用于存储指向这些可调用对象的"指针"
    '构建从运算符到函数指针的映射关系,其中函数接收俩个int、返回一个int' 
    map<string, int(*)(int, int)> b;
    
    'add是一个指向正确类型函数的指针,{'+',add}是一个pair'
    'add本身就是函数当传到int(*)(int, int)会被解析成指针'
    
    b.insert({"+", add});   //正确
    b.insert({"%", mod});   //错误:lambda表达式不是指针
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • function 定义在 functional 头文件中function 是一个模板,创建具体的 function 类型时要指明具体的调用形式。
    'function操作'
    function<retType(args)> f;            //f是一个用来存储可调用对象的空function,这些可调用对象的调用形式应该与类型T相同。
    function<retType(args)> f(nullptr);   //显式地构造一个空function
    function<retType(args)> f(obj)        //在f中存储可调用对象obj的副本
    f                                     //将f作为条件:当f含有一个可调用对象时为真;否则为假。
    f(args)                               //通过 f 调用 f 中的对象 
    
    'function中定义的类型'
    result_type          //该 funciton 类型的可调用对象返回的类型 retType
    argument_type        //retType(args) 中的参数类型
    first_argument_type  //retType(args) 中第一个参数的类型
    second_argument_type //retType(args) 中第二个参数的类型
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • function
      • 表示可以接收俩个int,返回一个int的可调用对象。因此可以用这个新生命的类型表示任意一种桌面计算机用到的类型

    在这里插入图片描述

    map<string, int(*)(int, int)> binops;//这种方式只能存储函数和函数指针,不能存储函数对象类和 lambda 表达式
    binops.insert("+",add);//正确
    
    map<string, function<int(int, int)>> binops = {  //可以存储相同调用形式的各种可调用对象
        {"-", std::minus<int>()},  //标准库函数对象
        {"/", divide()},           //用户定义的函数对象
        {"%", mod}                 //命名了的 lambda 对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    重载的函数与function

    • 不能直接将重载函数的名字存入 function 类型的对象中,但是可以存储指向确定重载版本的函数指针。
    int add(int i, int j){return i + j;}
    Sales_data add(const Sales_data&, const Sales_data&);
    map<string, function<int(int, int)>> b;
    b.insert({"+", add});  //错误:哪一个add
    
    '1. 存储函数指针消除二义性'
    int(*fp)(int, int) = add;
    b.insert({"+", fp});
    
    '2. 使用lambda消除二义性'
    b.insert({"+", [](int a, int b){return add(a, b);}});//因为lambda内部传入俩个int,因此调用只能匹配接收俩个int的add版本
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    😪14.9 重载、类型转换与运算符

    • 转换构造函数和类型转换运算符共同定义了类类型转换,这是用户定义的类型转换。
    • 构造函数会实现从其他类型向类类型的转换,类型转换运算符实现从类类型向其他类型的转换。
    • 只接受一个单独实参的非显式(即非 explicit 的)构造函数定义了从实参类型向类类型的类型转换。

    😡14.9.1 类型转换运算符

    • 类型转换运算符是类的一种特殊成员函数,用来将一个类类型的值转换成其他类型
    • 可以转换成除了 void 外任意可以作为函数返回值的类型,包括引用、指针,但是不可以转换为数组或者函数类型
    • 类型转换函数形式
      • operator type()cosnt;

    注意

    • 类型转换函数必须是类的成员函数,不能声明返回类型,形参列表也必须为空,且函数一般是 const 的。虽然不指定返回类型,但是函数会返回一个对应类型的值。
    • 函数名实际上就是返回类型
    class SmallInt {
    public:
        SmallInt(int i=0): val(i) {}
        operator int() const { return val}; //类型转换函数
    private:    
        int val;    
    }
    
    '类型转换运算符不需要显式调用,在执行运算时会隐式的执行。'
    SmallInt si;
    si = 4; //将 4 隐式地转换为 SmallInt,然后调用 SmallInt::operator
    si + 3; //将 si 隐式地转换为 int,然后执行整数的加法
    
    '也可以使用类型转换运算符将一个SmallInt对象转换为int''然后再将所得的int转换成任何其他算术类型'
    SmallInt si = 3.24;  //调用SmallInt(int)构造函数,将si转换为int
    si + 3.14            //内置类型转换将int转换为double
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    几种错误写法

    class B;
    operator int(&B);                     //错误:不是成员函数
    class B{
    public:
    	int operator int() cosnt;         //错误:指定了返回类型
    	operator int(int = 0) const;      //错误:参数列表不为空
    	operator int*() const (return 6); //错误:6不是一个指针
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 如果类类型和转换类型间没有明显的映射关系,不要用类型转换运算符。但是向 bool 的类型转换比较常见。
    //当istream含有向bool的类中转换时
    int i = 42;
    cin << i;  //如果向bool的类型转换不是显示的,则编译器看来是合法的
    //提升后的bool值(1或0)最终被左移42个位置
    
    • 1
    • 2
    • 3
    • 4

    显示的类型转换运算符

    • 用关键字 explicit 来将运算符指定为显式的,调用时需使用 static_cast 来强制显式转换。
    • 为了防止上述异常,使用显示的类型转换运算符来避免不合适的场合发生转换

    注意

    • 如果表达式被用作条件,则显式的类型转换会被隐式地执行。如用在 if 语句的条件部分。
    class SmallInt {
    public:
        explicit operator int() const { return val; }
        // 类的其他部分省略。    
    }
    
    SmallInt si = 4;            //正确:构造函数不是显式的,可以隐式将int类型转换为类类型
    si + 3;                     //错误:此处需要隐式的类型转换,但类的运算符是显式的
    static_cast<int>(si) + 3;   //正确:显式地请求类型转换(强制转换)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    转换为bool类型

    • 向 bool 类型的转换一般都用于条件部分,因此 operator bool() 一般定义成 explicit 的
    while(std::cin >> value)
    
    • 1

    它负责将数据读入到value并返回cin.cinistream operator bool类型转换函数隐式的执行了转换,转换为bool类型

    😡14.9.2 避免有二义性的类型转换

    • 要确保类类型和目标类型之间只存在唯一的转换方式。
    • 有两种情况可能产生多重转换路径:
      1. 两个类提供相同的类型转换。A 类定义了一个接受 B 类对象的转换构造函数,同时 B 类定义了一个转换目标是 A 类的类型转换运算符。
      2. 定义了多个转换规则。

    通常情况下,不要为类定义相同的类型转换,也不要在类中定义俩个及俩个以上转换源或转换目标是算术类型的转换
    在这里插入图片描述

    转换目标为内置类型的多重类型转换

    • 不要令两个类执行相同的类型转换
    • 避免转换目标是内置算数类型的类型转换。特别是已经定义了一个转换成算数类型的类型转换时。

    在这里插入图片描述

    重载函数与转换构造函数

    在这里插入图片描述
    重载函数与用户定义的类型转换

    在这里插入图片描述

    😡14.9.3 函数匹配与重载运算符

    在这里插入图片描述

  • 相关阅读:
    【HMS core】【FAQ】Analytics Kit、Push Kit典型问题合集3
    【SQL server速成之路】触发器
    计算机毕业设计之java+javaweb的美容院管理系统
    AD20原理图库的制作
    高云FPGA系列教程(4):片上逻辑分析仪GAO的使用
    AntDesignBlazor示例——暗黑模式
    Linux之grep查找文本时匹配反斜杠\转义问题
    【Java】Java易丢失的基础知识一
    14.(vue3.x+vite)组件间通信方式之pinia
    C# 委托学习1
  • 原文地址:https://blog.csdn.net/weixin_45043334/article/details/126679427