• (十四)重载运算符与类型转换



    运算符作用于 类类型对象时,可以通过运算符重载重新定义运算符含义。

    1重载运算符基本概念

    1.1定义

    • 重载运算符是具有特殊名字的函数:operator+运算符
    • 重载运算符参数:

    1.重载运算符参数数目和运算对象数目一样多,左侧第一个右侧第二个。
    2.当运算符函数是成员函数时,左侧绑定到this上。此时重载运算符参数数目比运算对象数目少一个。
    3.重载运算符不含有默认实参

    • 运算符函数或者是类的成员,或者至少含有一个类类型参数。当运算符作用于内置类型运算对象时,无法改变运算符含义。
    • 可重载运算符:
    +-*/%^
    &|~!,=
    <><=>=++- -
    <<>>==!=&&||
    +=-=/=%=^=&=
    |=*=<<=>>=[]()
    ->->*newnew[]deletedelete[]
    • 不可重载运算符:不能发明新运算符号
    ::.*.?:
    • 通常情况下,不重载",“,”&“,”&&“,”||"运算符。
    • 重载运算符的优先级和结合律与对应内置运算符保持一致。

    1.2调用

    data1 += data2; //间接调用
    operator+=(data1,data2); //直接调用
    data1.operator+=(data2);
    
    • 1
    • 2
    • 3

    1.3注意

    • 使用与内置类型一致的含义,逻辑上与运算符相关。
    • 当重载了一个运算符,其相关运算符可能也需要重载,比如重载了operator=,相应也要有operator!=。
    • 重载运算符返回类型应与其内置版本兼容。

    1.4作为成员函数还是非成员函数的选择

    • 当把运算符定义成成员函数时,左侧运算对象必须是运算符所属类的一个对象。其中"=“,”[]“,”()“和”->“必须是成员。对于改变对象状态或与给定类型密切相关的运算符,通常是成员,比如复合赋值(”+=“等),”–“,”++“,”&"等。
    • 具有对称性的运算符(可以转换任意一端的运算对象,类型上有混用),应该是普通的非成员对象,如算数、相等性、关系和位运算等。

    2运算符重载实现

    2.1输入输出运算符

    • IO标准库使用>>和<<执行输入输出操作。
    • 输入输出运算符必须是非成员函数。因为左侧运算对象是istream&/ostream&
    • 考虑IO运算符需要对写非公有数据成员,IO运算符一般声明为友元对象。

    2.1.1重载输出运算符<<

    //类中友元声明
    friend	std::ostream& operator<<(std::ostream&, const hhdy&);
    //非成员函数
    std::ostream& operator<<(std::ostream& os, const hhdy& h) {  //两个都是引用
    	os << h.grades << h.item;
    	return os;  //返回ostream形参
    }
    //主函数使用
    	hhdy hh(60, "xx");
    	std::cout << hh<<std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 输出运算符主要负责打印对象内容,但不会考虑格式化操作(比如换行符),使用户能控制输出的细节。

    2.1.2重载输入运算符>>

    //友元声明
    friend std::istream& operator>>(std::istream&, hhdy&);
    //非成员函数
    std::istream& operator>>(std::istream& is,  hhdy& h) {
    	is >> h.grades >> h.item;
    	if (!is) {  //判断输入是否成功
    		h = hhdy();  
    	}
    	return is;
    }
    //主函数使用
    do {		
    		std::cin >> hh;
    		if (!hh.empty()) { 
    			hvec.push_back(hh);
    		}		
    	} while (!hh.empty());
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 输入运算符需要处理输入失败的情况。
    • 当读取发生错误时,输入运算符应从错误中恢复
    • 最好能通过里流标识符表示出失败信息。

    2.2算术和关系运算符

    • 定义为非成员函数,形参都是常量引用
    • 一般有算术运算符也会有复合赋值运算符,则通常使用复合赋值来定义算术运算符
    //友元声明
    	friend hhdy operator+(const hhdy& a, const hhdy& b);
    //非成员函数
    hhdy operator+(const hhdy& a, const hhdy& b) {
    	hhdy sumh = a;
    	sumh.grades = a.grades + b.grades;
    	return sumh;
    }
    //主函数使用
    	hhdy h1(60, "xx");
    	hhdy h2(80, "xx");
    	hhdy h3 = h1 + h2;
    	std::cout << h3 << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.3相等运算符

    • 非成员
    • 检验两个对象是否相等,有相等就有不相等运算符
    • operator==operator!=
    //友元声明
    	friend bool operator==(const hhdy& a, const hhdy& b);
    	friend bool operator!=(const hhdy& a, const hhdy& b);
    
    //非成员函数
    	bool operator==(const hhdy& a, const hhdy& b) {
    		return a.grades == b.grades && a.item == b.item;
    	}
    	bool operator!=(const hhdy& a, const hhdy& b) {
    		return !(a == b);
    	}
    //主函数使用
    	std::cout << (h1 == h3) << std::endl;
    	std::cout << (h1 != h3) << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.4关系运算符

    • 非成员
    • operator<operator>operator<=operator>=
    • 关系运算符需满足以下要求:

    1.定义顺序关系
    2.关系应当与"“运算符相一致
    3.当只存在唯一一种逻辑可靠的”<“定义,或者”<“定义与”
    "运算符一致时才定义相关关系运算符

    2.5赋值运算符

    • 不是必须定义为成员函数,倾向于定义为成员函数
    • 列表赋值运算符:以花括号元素列表为参数的赋值运算符
    • 复合赋值运算符:"operator+="等
    • 注意赋值和赋值初始化区别
    //列表赋值运算符
    //头文件
    #include
    //声明
    	hhdy& operator=(std::initializer_list<std::string> il)
    
    //成员函数
    	hhdy& operator=(std::initializer_list<std::string> il) {
    			grades = 100;
    			std::string str;
    			for (auto& s : il) {
    				str = str + s;
    			}
    			item = str;
    			return *this;
    		}
    //主函数使用
    	hhdy h4;
    	h4 = { "aaa","bbb" };
    	hhdy h5 = { "aaa","bbb" };  //这个是赋值初始化
    
    //复合赋值运算符
    //声明
    	hhdy& operator+=(const hhdy& h) 
    
    //成员函数
    	hhdy& operator+=(const hhdy& h) {
    		this->grades = this->grades + h.grades;
    		return *this;
    	}
    	hhdy operator+( const hhdy& h1,const hhdy& h2){
    		hhdy hsum = h1;
    		hsum += h2;
    		return hsum;
    	}
    //主函数使用
    	hhdy gradesum(0, "sum");
    	gradesum += h1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.6下标运算符

    • 必须是成员函数
    • 通过元素在容器中的位置访问元素operator[]
    • 返回值是所访问元素的引用
    • 通常会同时定义下标运算符的常量和非常量版本
    //声明
    	int& operator[](std::size_t n); 
    	const int& operator[](std::size_t n)const;
    
    //成员函数
    	int& operator[](std::size_t n) {
    		return number[n];
    	}
    	const int& operator[](std::size_t n)const {
    		return number[n];
    	}
    //主函数使用
    	std::vector<int> vi = { 1,2,3,4 };
    	zwhy xx(vi, "xx");
    	std::cout << xx[2] << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.7递增递减运算符

    • 不必要但建议设定为成员函数
    • 需要同时定义前置版本和后置版本,相比前置版本,后置版本多了一个int类型形参但不使用。
    • 后置版本递增之前需要先记录对象状态
    //前置版本
    //头文件
    	#include
    //声明
    	hhdyzwhy& operator++();
    	hhdyzwhy& operator--();
    
    //成员函数
    	hhdyzwhy& operator++() {
    		if (check(curr+1, "increment past end of StrBlobPtr")) {
    			++p;
    			++curr;			
    		}
    		return *this;
    	}
    	hhdyzwhy& operator--() {
    		if (check(curr-1, "decrement past begin of StrBlobPtr")) {
    			--p;
    			--curr;
    		}
    		return *this;
    	}
    //主函数使用
    	std::vector<int> vi = { 1,2,3,4 };
    	hhdyzwhy hhxx(vi);
    	std::cout << hhxx.getval() << std::endl;
    	++hhxx;
    	std::cout << hhxx.getval() << std::endl;
    	hhxx.operator++();//前置版本递增调用
    
    //后置版本
    //头文件
    	#include
    //声明
    	hhdyzwhy& operator++(int);
    	hhdyzwhy& operator--(int);
    
    //成员函数
    	hhdyzwhy operator++(int) {
    		hhdyzwhy temp = *this;
    		if (check(curr + 1, "increment past end of StrBlobPtr")) {
    			++p;
    			++curr;
    		}
    		return temp;
    	}
    	hhdyzwhy operator--(int) {
    		hhdyzwhy temp = *this;
    		if (check(curr - 1, "increment past end of StrBlobPtr")) {
    			--p;
    			--curr;
    		}
    		return temp;
    	}
    //主函数使用
    	std::vector<int> vi = { 1,2,3,4 };
    	hhdyzwhy hhxx(vi);
    	std::cout << hhxx++.getval() << std::endl;
    	std::cout << hhxx.getval() << std::endl;
    	hhxx.operator++(0);//后置版本递增调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    2.8成员访问运算符

    • 解引用运算符(*)和箭头运算符(->)
    • p->size等价于(*p).size
    • 相比于解引用直接返回成员对象对应内容,箭头运算符使用时必须通过"->"运算符获取成员,在访问成员中内容。
    解引用运算符箭头运算符
    建议是类成员,可以不是必须是类成员
    返回所指元素的引用调用解引用运算符返回解引用结果元素地址
    • 对于成员访问运算符:

    箭头运算符必须返回类的指针或者自定义箭头运算符的某个类对象。
    1.如果返回类指针,使用内置箭头运算符。
    2.如果返回某个类对象,使用"point.operator->()"来的结果来获取类A中类B的对象,如果是指针,则进行第一步。如果类中还有重载箭头运算符,则继续调用该运算符。

    //声明
    	hhdyzwhy& operator++(int);
    	hhdyzwhy& operator--(int);
    
    //成员函数
    	//声明
    	std::string& operator*() const;
    	std::string* operator->() const;
    
    //成员函数
    	std::string& operator*() const {
    		return(*p);
    	}
    	std::string* operator->() const{
    		return &(this->operator*()); //这里调用的是类中对象的成员函数,这里是指向对象的成员函数
    	}
    //主函数使用
    	std::vector<std::string>vstr = { "xx","hh","zwhy","hy"};
    	std::cout << hhxx.getval()<< std::endl;  //xx
    	std::cout << *hhxx << std::endl; //xx
    	std::cout << hhxx->size() << std::endl; //2,这里调用的是类中对象的成员函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.9函数调用运算符

    • 必须是成员函数
    • 可以像使用函数一样使用该类对象。一个类可以定义多个不同版本的调用运算符,相互之间在参数数量和类型上应有所区别。
    //输出一个值
    	//类
    	class absInt {
    	public:
    		int operator()(int a) { //输出一个值
    			return a > 0 ? a : -a; 
    		}
    	};	
    	//主函数使用
    	int a = -5;
    	absInt getabs;
    	int b = getabs(a); //向调用函数一样调用类对象
    	std::cout << a << "," << b << std::endl;
    	
    //输入输出流应用
    	//类
    	class printstring {
    	public:
    		printstring(ostream& s = cout, string str = " "):os(s),sep(str){};
    		void operator()(const string& s)const { os << s << sep; }
    	private:
    		ostream& os;
    		string sep;
    	};
    	//主函数使用,依次输出
    	printstring printstr(cout, " ");
    	vector<string>vs = { "ss","xx","hh","zwhy" };
    	printstr(vs[1]);
    	cout << endl;//xx
    	for_each(vs.begin(), vs.end(), printstring(cout, ",")); //第三个实参是printstring的一个临时对象。ss,xx,hh,zwhy,
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    lamda表达式的实现

    • 编译器会将lambda表达式翻译成一个未命名类的未命名对象,此类中含有一个重载的函数调用表达式。
    • 默认情况下,lambda不能改变它捕获的对象,所以是const成员函数。当lambda声明为可变时(mutable),调用运算符就不是const得了。
    • 当lambda表达式通过引用捕获变量时,编译器可以直接使用,无需在lambda产生的类中将其存储为数据成员。当lambda表达式通过值拷贝到lambda中时,需要建立对应的数据成员。
    • lambda表达式产生的类不含默认构造函数、赋值运算符以及默认析构函数,是否默认拷贝/移动是捕获数据成员类型而定
    //lambda表达式泛型函数
    	vector<string>vs = { "ss","xx","hh","zwhy" };
    	size_t sz = 3;
    	auto wc = find_if(vs.begin(), vs.end(), [sz](const string& a) {return a.size() > sz; }); 
    	cout << *wc << endl;//zwhy
    //对应类
    	class sizecomp {
    	public:
    		sizecomp(size_t n):sz(n){}
    		bool operator()(const string& a)const {
    			return a.size() > sz;
    		}
    	private:
    		size_t sz;
    	};
    	auto sc = find_if(vs.begin(), vs.end(), sizecomp(sz));
    	cout << *sc << endl;	//zwhy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    标准库定义的函数对象

    • 标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类中定义了对应命名的调用运算符。
    • 头文件#include
    算术关系逻辑
    plusequal_tological_and
    minusnot_equal_tological_or
    multipliesgreaterlogical_not
    dividesgreater_equal
    modules %less
    negate*-1less_equal
    • 函数对象对于指针同样适用。对于关联容器可以使用function进行元素排序。
    //模板调用操作
    	int a1 = 10, b1 = 20;
    	plus<int> intsum;
    	int c = intsum(a1, b1);
    	cout << c<<endl; //30
    //泛型算法中使用
    	sort(vs.begin(), vs.end(), greater<string>());
    	for_each(vs.begin(), vs.end(), [](const string& a) {cout << a << " "; });//zwhy xx ss hh
    	cout << endl;
    //单参数情况:bind 
    	vector<int> s = { 1,2,3,4,5,4,3,2 };
    	sort(s.begin(), s.end(), greater<int>());
    	cout << count_if(s.begin(), s.end(), bind2nd(greater<int>(),3)) << endl;//3
    	cout << count_if(s.begin(), s.end(), bind1st(modulus<int>(), 60)) << endl;//0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可调用对象和lambda

    • 可调用对象:调用对象包括函数、函数指针、lambda表达式、bind对象、重载函数调用运算符的类等。调用对象之间可能共享同一种调用形式(调用返回类型以及传递给调用的实参类型),比如int(int,int):
    int add(int i,int j){return i+j;}
    auto mod = [](int i,int j){return i%j;};
    struct divide{
    	int operator()(int i,int j){returni/j;}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 某些情况下可能想制作可调用函数的函数表,存储可调用对象的“指针”,查找并使用对应函数。
    1. map实现
    2. function类型实现
    • map实现:通过map形成string-可调用对象指针键值对。调用string索引map。不能将lambda表达式或重载可调用函数对象的类存入map中,因为他们有自己的类类型。(测试lambda表示能用)
    	map<string, int(*)(int, int)>binops;
    	binops.insert({ "+",add });
    	cout << binops["+"](1, 2) << endl;
    	binops.insert({ "%",mod });  //测试这个可以
    	divide div; 
    	binops.insert({ "/",div }); //这个不行,类型是divide
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • function类型:未解决以上问题,可以使用function类型,头文件#include
    操作说明
    functionalf;用来存储可调用对象的空function
    functional(nullptr);显式构造一个空function
    functional(obj);存储可调用对象obj的副本
    ff作为条件
    f(args)调用函数对象
    定义为function成员类型
    result_typefunction类型可调用对象返回类型
    argument_type一个实参时实参类型
    first_argument_type两个实参时,第一个实参类型
    second_argument_type两个实参时,第二个实参类型
    	function<int(int, int)>f1 = add;
    	function<int(int, int)>f2 = divide();
    	function<int(int, int)>f3 = [](int i, int j) {return i * j; };
    	cout << f1(4, 2) << endl;//6
    	cout << f2(4, 2) << endl;//2
    	cout << f3(4, 2) << endl;//8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 使用function定义map
    • 对于重载的函数,可以存储函数指针再加入到map中。或者使用lambda消除二义性。
    	int(*fp)(int, int) = add;
    	map<string, function<int(int, int)>> binops2 = {
    		{"+",fp},{"-",minus<int>()},{"*",[](int i,int j) {return i * j; }},
    		{"/",divide()},{"%",mod}
    	};
    	cout << binops2["+"](4, 2) << endl;
    	cout << binops2["-"](4, 2) << endl;
    	cout << binops2["*"](4, 2) << endl;
    	cout << binops2["/"](4, 2) << endl;
    	cout << binops2["%"](4, 2) << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.10 类型转换运算符

    • 可以定义对类类型的类型转换,这是一种特殊的成员函数operatorconst;。Type为函数的返回类型,不允许转换为数组或者函数类型,但可以转换为指针或者引用。类型转换运算符不能声明返回类型,形参列表也为空,通常不应该改变转换对象的内容,一般会被定义为const成员。
    • 当类型转换存在多种语义时,建议通过普通成员函数提取,而不是通过类型转换。
    class smallint {
    public:
    	smallint(int i = 0):val(i) {
    		if (i < 0 || i>255)
    			throw out_of_range("out of smallint range");
    		}
    	operator int() {
    		return val;
    	}
    private:
    	size_t val;
    };
    int main(){
    	smallint a = 4;
    	cout << a + 2 << endl;//6
    	smallint b = 3.14; //double->size_t
    	cout << b + 3.14 << endl;//6.14size_t->int->double
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 某些情况下类型转换可能会发生意外结果:
    int i = 42;
    cin << i;
    //这里cin应该是>>才对,在这种情况下,cin内的值会类型转换为bool(1/0),再左移42位。
    
    • 1
    • 2
    • 3

    显式类型转换运算符

    • 为避免意外结果的发生,可以使用显式的类型转换运算符
    //显式类型转换
    	explicit operator int() {
    		return val;
    	}
    smallint a = 4;
    a+2; //错误这里是隐式
    static_cast<int>(a)+2; //正确,显式的强制类型转换
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 如果表达式被用作条件,编译器会将显式类型转换自动应用于它,对于显式用做条件必须有bool类型转换符,否则两次转换会报错:

    if、while及do语句条件部分
    for语句头条件表达式
    逻辑与或非运算符运算对象
    条件运算符(?:)

    //对于b是smallint类型,显式声明装换为int类型
    if (b) { cout << "true" << endl; } //这里用b会报错,因为将b中b.val显式转换为int类型,另外还要一步转换为bool类型才能进行判断。
    
    • 1
    • 2
    • 对于IO类型:早期版本定义了向void*转换规则,C++11标准下,通过定义一个向bool的显式类型转换实现同样目的。在条件中使用流对象,会使用IO类型定义的operator bool()
    int value;
    auto cinstate = (cin >> value).rdstate(); //数据读入到value并返回cin,可以通过rdstate()函数返回cin状态
    while(cin >> value) //此时返回的cin会隐式转换为bool形式
    
    • 1
    • 2
    • 3
    • bool类型转换通常定义为explicit形式。

    避免二义性类型转换

    • 要确保类类型和目标类型之间只存在唯一种转换方式。

    1.两个类提供了相同的类型转换:

    • A类定义了接受B类的转换构造函数同时B类存在到A类的转换运算符。此时无法使用强制类型转换解决二义性问题,因为强制类型转换本身也具有二义性。
    struct A {
    	A() = default;
    	A(B& b) {}
    };
    struct B {
    	operator A(){}
    };
    
    int main(){
    	B b1;
    	A a1 = b1; //报错,存在两种从类型B到类型A的方法
    	//必须显示调用构造函数或转换运算符
    	A a2 = A(b.operator A());
    	A a3 = A(b);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.定义了多个转换规则:

    • 存在转换级别一致的情况
    struct C {
    	C(int c = 0) { cout << "initint" << endl; }
    	C(double c = 0.0) { cout << "initdouble" << endl; }
    	operator int() { cout << "toint" << endl; }
    	operator double() { cout << "todouble" << endl; }
    };
    int main(){
    	long l;
    	long double ld;
    	short s;
    	
    	C c1(l); //报错
    	C c2(ld); //报错
    	C c3(s); //转换为int
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.多个类的类型转换具有同一类可行匹配,则这些类型转换一样好。

    • 调用重载函数时,不建议使用构造函数或者强制类型转换来改变实参类型。
    • 调用重载函数时,如果多个用户定义的类型转换都提供了可行匹配,则认为这些类型一样好。即使一个需要额外的标准类型转换而另一个是精准匹配。此时不会考虑任何可能出现的标准类型转换级别。转换级别只有当所有可行函数都请求同一个用户定义的类型转换时才有用。
    struct D {
    	D(int) {};
    };
    struct E
    {
    	E(int) {};
    };
    struct F {
    	F(double) {};
    };
    void manip(const C&){}
    void manip(const D&){}
    void manip(const E&){}
    int main(){
    	manip(10); //报错,有三个可用重载形式
    	manip(D(10)); //正确,显示转换
    	manipu(F(double(10)));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.函数匹配与重载运算符

    struct H {
    	friend int operator+(H h1, H h2);
    	H(int h1 = 0) :h(h1) {}
    	operator int() { return h; }
    	H operator+(H h2) { cout << "成员函数"<<endl; return H(h + h2.h); }
    		int h;
    };
    int operator+(H h1, H h2) {
    	cout << "非成员函数" << endl;
    	return h1.h + h2.h;
    }
    int main(){
    	H hh(2),xx(3);
    	cout << hh + xx << endl; //这种情况既考虑成员函数,又考虑非成员函数,二义性
    	cout << (hh.operator+(xx)).h<< endl; //这种情况只考虑成员函数
    	cout << 3 + hh << endl;//非成员函数与内置函数二义性(int可以转为H,H可以转为int)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    K8s(Kubernetes)学习(四):Controller 控制器:Deployment、StatefulSet、Daemonset、Job
    基于未知环境碰撞冲突预测的群机器人多目标搜索研究
    基于C#实现协同推荐 SlopeOne 算法
    java毕业生设计信管专业毕业生就业管理信息系统计算机源码+系统+mysql+调试部署+lw
    6. Python使用Asyncio开发TCP服务器简单案例
    洋葱架构、三层架构及两者区别
    【手写算法实现】 之 KNN K近邻算法
    GB28181学习(二)——注册与注销
    《Linux从练气到飞升》No.28 Linux中的线程同步
    刚装的vm虚拟机的 ubuntu要装很多东西
  • 原文地址:https://blog.csdn.net/qq_40212968/article/details/126298200