14 C++ 重载运算与类型转换


前言

提示:这里可以添加本文要记录的大概内容:


提示:以下是本篇文章正文内容,下面案例可供参考

基本概念

运算符函数

//错误:不能为int重新定义内置的运算符
int operator+(int, int);

只能重载已有的运算符,而无权发明新的运算符

可以被重载的运算符
+		-		*		/		%		^
&		|		~		!		,		=
<		>		<=		>=		++		--
<<	    >>	    ==  	!=	   	&&		||
+=  	-=		/=		%=	     ^=	     &=
|=		*=  	<<=	    >>=	     []	     ()
->		->*	    new		new[]	delete	delete[]
不能被重载的运算符
::		.*		.		? :

直接调用一个重载的运算符函数

//等价调用
data1 + data2;
operator+(data1, data2);

data1 += data2;
data1.operator+=(data2);

某些运算符不应该被重载
重载运算符的求值顺序等属性无法保留

使用与内置类型一致的含义

选择作为成员函数或者非成员
在这里插入图片描述
在这里插入图片描述

输入和输出运算符

ostream & operator<<(ostream &os, const Sales_data &item){
	os << item.isbn() << " " << item.units_sold << " " 
	<< item.revenue << " " << item.avg_price();
	return os;
}

在这里插入图片描述
输入输出运算符必须是非成员函数

istream &operator>>(istream &is, Sales_data &item){
	double price;
	is >> item.bookNo >> item.units_sold >> price;
	if (is)	//检查输入是否成功
		item.revenue = item.units_sold * price;
	else 	//输入失败:对象被赋予默认的状态
		item = Sales_data();
	return is;
}

输入运算符必须处理输入可能失败的情况,而输出运算符不需要。

istream& operator>>(istream& is, Test&t){
    is >> t.a;
    if (is)
        return is;
    else
        t.a = 999;
    return is;
}

算数和关系运算符

Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs){
	Sales_data sum = lhs;
	sum += rhs;
	return sum;
}

相等运算符

bool operator==(const Sales_data &lhs, const Sales_data &rhs){
	return lhs.isbn() == rhs.isbn() && 
			  lhs.units_sold == rhs.units_sold &&
			  lhs.revenue == rhs.revenue;
}
bool operator!=(const Slaes_data &lhs, const Sales_data &rhs){
	return !(lhs == rhs);  //yeah!!!
}

关系运算符

赋值运算符

拷贝赋值和移动赋值运算符
再加个

class StrVec{
public:
	StrVec &operator=(std::initializer_list<std::string>);
};
StrVec& StrVec::operator(initializer_list<string> il){
	// alloc_n_copy分配内存空间并从给定范围内拷贝元素
	auto data = alloc_n_copy(il.begin, il.end());
	free(); 				//销毁对象中的元素并释放内存空间
	elements = data.first;	//更新数据成员使其指向新空间
	first_free = cap = data.second;
	return *this;
}

可以重载赋值运算符,不论形参的类型是什么,赋值运算符必须定义为成员函数

复合赋值运算符
与赋值运算符相差无几

下标运算符

class StrVec{
public:
	std::string& operator[](std::size_t n){
		return elements[n];
	}
	const std::string& operator[](std::size_t n) const{
		return elements[n];
	}
	//...
};

递增递减运算符

前置递增递减

class StrBlovPtr{
public:
	//前置
	StrBlobPtr& operator++();
	StrBlobPtr& operator--();
	//后置, int仅用于标识后置
	StrBlobPtr operator++(int);
	StrBlobPtr operator--(int);
	
};
//前置版本:返回递增/递减对象的引用
StrBlobPtr& StrBlobPtr::operator++(){
	//如果curr已经指向了容器的尾后位置,则无法递增它
	check(curr, "increment past end of StrBlobPtr");
	++curr;
	return *this;
}
StrBlobPtr& StrBlobPtr::operator--(){
	//如果curr是0,则继续递减它将产生一个无效的下标
	--curr;
	check(curr, "decrement past begin of StrblobPtr");
	return *this;
}
//后置版本
StrBlobPtr StrBlobPtr::operator++(int){
	//此处无需检查有效性,调用前置递增运算时才需要检查
	StrBlobPtr ret = *this;		//记录当前值
	++*this;					//前向移动一个元素,前置++需要检查递增的有效性
	return ret;					//返回之前记录的状态
}
StrBlobPtr StrBlobPtr::operator--(int){
	//此处无需检查有效性,调用前置递减运算时才需要检查
	StrBlobPtr ret = *this;		//记录当前值
	--*this;					//前后移动一个元素,前置--需要检查递减的有效性
	return ret;					//返回之前记录的状态
}

//显示调用
p.operator++(0); //调用后置版本的++
p.operator++( ); //调用前置版本的++

此处int形参并不真正使用,仅用来区分前后置
注意

const StrBlobPtr StrBlobPtr::operator++(int){
	StrBlobPtr ret = *this;		
	++*this;					
	return ret;	
// cosnt 可以防止如下的调用
Test tst(9);
Test a =( tst ++ ++ );//报错
cout<< a << " "<<tst;

成员访问运算符

class StrBlovPtr{
public:
	std::string& operator*() const{
		auto p = check(curr, "dereference past end")
		return (*p)[curr];	//(*p)是对象所指的vector
	}
	std::string* operator->() const{
		//将实际工作委托给解引用运算符
		return & this->operator*();
	}
};

在这里插入图片描述
对箭头返回值的限定
在这里插入图片描述

函数调用运算符

struct absInt{
	int operator()(int val) cosnt {
		return val < 0 ? -val : val;
	}
};
int i = -42;
absInt absObj;	
int ui = absObj(i); //将i传递给absObj.operator()

函数调用运算符必须是成员函数,可以重载
absObj称为函数对象function object ,“行为像函数一样”

class PrintString{
public:
	PrintString(ostream &o = cout, char c = ' '):
		os(o), sep(c) {}
	void operator()(cosnt string &s) const {os << s << sep;}
private:
	ostream *os;	//用于写入的目的流
	char sep;		//用于将不同输出隔开的字符
};
PrintString printer;	//使用默认值,打印到cout	
printer(s);				//在cout中打印s,后面跟一个空格
PrintString errors(cerr, '\n');
errors(s);				//在cerr中打印s,后面跟一个换行符

for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));

lambda是函数对象

当编写了一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。在lambda表达式产生的类中含有一个重载的函数调用运算符。

stable_sort(wds.begin(), wds.end(), 
			[](const string &a, const string &b){return a.size() < b.size();} );
//其行为类似于下面这个类的一个未命名对象
class ShorterString{
public:
	bool operator()(const string&s1, const string&s2)const
	{ return s1.size()<s2.size(); }
};
//用上面的类代器
stable_sort(wds.begin(), wds.end(), ShorterString() );

表示lambda及相应捕获行为的类

//获得第一个指向满足条件元素的迭代器,该元素满足size() is >= sz
auto wc = find_if(wds.begin(), wds.end(), 
				[sz](cosnt string &a){return a.size() >= sz;} );
//该lambda表达式产生的类将形如:
class SizeComp{
public:
	SizeComp(size_t n):sz(n) { } //该形参对应捕获的变量
	//该调用运算符的返回类型、形参和函数体都与lambda一致
	bool operator()(const string &s)const
	{return s.size() >= sz;}
private:
	size_t sz;
};
auto wc = find_if(wds.begin(), wds.end(), SizeComp(sz) );

标准库定义的函数对象

<functional.h>头文件中
在这里插入图片描述

plus<int> intAdd;		//可执行int加法的函数对
negate<int> intNegate;	//可对int值取反的函数对
//使用intAdd::operator(int, int)求和
int sum = intAdd(10, 20); //等价于sum=30
sum = intNegate(intAdd(10, 20));
//
sum = intAdd(10, intNegate(10)); //sum=0

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

//降序排序
sort(svec.begin(), svec.end(), greater<string>() );
//greater<string>() 为greater类型的一个未命名的对象

vector<string*> nameTable;  //指针的vector
//错误❌:nameTable中的指针彼此之间没有关系,所以<将产生未定义的行为
sort(nameTable.begin(), nameTable.end(),
	[](string*a, string*b) {return a<b;} );
//正确:标准库规定指针的less是定义良好的
sort(nameTable.begin(), nameTable.end(), less<string*>() );

关联容器使用less<key_type>对元素排序,因此我们可以定义一个指针的set或者在map中使用指针作为关键值而无需直接声名less

可调用对象与function

不同类型可能具有相同的调用形式

//普通函数
int add(int i, int j){ return i + j; }
//lambda, 其产生一个未命名的函数对象类
auto mod = [](int i, int j){ return i%j; };
//函数对象类
struct divide{
	int operator()(int denominator, int divisor)
	{ return denominator / divisor;}
};

调用形式:int(int, int)

//构建从运算符到函数指针的映射关系,其中函数接受连个int、返回一个int
map<string, int(*)(int, int)> binops;
//正确:add是一个指向正确类型函数的指针
binops.insert({"+", add});
//错误:mod不是一个函数指针
//mod是个lambda表达式,而每个lambda有它自己的类类型,该类型与binops不符
binops.insert({"%", mod}); //❌

标准库function类型
<functional.h>
在这里插入图片描述

function<int(int, int)> f1 = add;  //函数指针
function<int(int, int)> f2 = divide();  //函数对象的类
function<int(int, int)> f3 = [](int i, int j) //lambda
							   { return i*j;};
cout << f1(4,2) << endl;
cout << f2(4,2) << endl;
cout << f3(4,2) << endl;

map<string, function<int(int, int)>> binops = {
	{"+", add},
	{"-", std::minus<int>()},
	{"*", [](int i,int j){return i*j;}},
	{"/", divide()},
	{"%", mod}
};

binops["+"](10,5);

//有个问题
int add(int i, int j){return i + j;}
double add(double i, doublej);
binops.insert( {"+", add} ); //错误:哪个add??
//消除二义性
int (*fp)(int, int) = add;
binops.insert({"+", fp});
binops.insert( {"+", [](int a, int b){return add(a, b);} } );

重载、类型转换与运算符

类类型转换class-type conversions

类型转换运算符

类型转换运算符conversion operator是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。
operator type() const;
一个类型转换函数必须是类的成员函数;它不能声名返回类型,形参列表也必须为空。类型转换函数通常应该是const

class SmallInt{
public:
	SmallInt(int i=0):val(i)
	{ if ( i<0 || i>255) throw std::out_of_range("Bad SmallInt value"); }
	operator int() const {return val;}
private:
	std::size_t val;
};

SmallInt si;
si = 4;	//首先将4隐式的转换成SmallInt,然后调用SmallInt::operator=
si + 3; //首先将si隐式的转换成int, 然后执行整数的加法

//内置类型转换将double实参转换成int
SmallInt si = 3.14;	//调用SmallInt(int)构造函数
//SmallInt 的类型转换运算符将si转换成int
si + 3.14;          //内置类型转换将所得的int继续转换成double

class SmallInt;
operator int(SmallInt&); //错误,不是成员函数
class SmallInt{}
public:
	int operator int() const;		   //❌指定了返回类型
	operator int (int = 0) const;	   //❌参数列表不为空
	operator int*() const {return 42;} //❌42不是一个指针

显示的类型转换运算符explicit conversion operator

避免右二义性的类型转换

函数匹配与重载运算符

a sym b
//以下都具有二义性
a.operator\sym(b);	//a有一个operatorsym成员函数
operator\sym(a,b);  //operatorsym是一个普通函数

在这里插入图片描述


总结

调用形式 call signature
类类型转换 conversion operator
类型转换运算符 conversion operator
显式的类型转换运算符 explicit conversion operator
函数对象 function object
函数表 function table
函数模板 function template
重载的运算符 overloaded operator

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值