栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
  • 前言
  • 基本概念
  • 输入和输出运算符
  • 算数和关系运算符
    • 相等运算符
    • 关系运算符
    • 赋值运算符
  • 下标运算符
  • 递增递减运算符
  • 成员访问运算符
  • 函数调用运算符
    • lambda是函数对象
    • 标准库定义的函数对象
    • 可调用对象与function
  • 重载、类型转换与运算符
    • 类型转换运算符
    • 避免右二义性的类型转换
    • 函数匹配与重载运算符
  • 总结


前言

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


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

基本概念

运算符函数

//错误:不能为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);
};
StrVec& StrVec::operator(initializer_list 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 << " "< 
成员访问运算符 
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() 

表示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) );
标准库定义的函数对象

头文件中

plus intAdd;		//可执行int加法的函数对
negate 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() );
//greater() 为greater类型的一个未命名的对象

vector nameTable;  //指针的vector
//错误❌:nameTable中的指针彼此之间没有关系,所以<将产生未定义的行为
sort(nameTable.begin(), nameTable.end(),
	[](string*a, string*b) {return a() );

关联容器使用less对元素排序,因此我们可以定义一个指针的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 binops;
//正确:add是一个指向正确类型函数的指针
binops.insert({"+", add});
//错误:mod不是一个函数指针
//mod是个lambda表达式,而每个lambda有它自己的类类型,该类型与binops不符
binops.insert({"%", mod}); //❌

标准库function类型

function f1 = add;  //函数指针
function f2 = divide();  //函数对象的类
function 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> binops = {
	{"+", add},
	{"-", std::minus()},
	{"*", [](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.operatorsym(b);	//a有一个operatorsym成员函数
operatorsym(a,b);  //operatorsym是一个普通函数


总结

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/869443.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号