C++ Primer学习笔记-----第十四章:重载运算与类型转换

这一章重载运算符和类型转换比较简单,直接看书理解就行。
但也比较容易出现错误,仔细看书就行。
重载运算符和类型转换是为了更好的使用类型,即使没有不使用这个技术也不影响。

在这里插入图片描述
下面说一下函数调用运算符:()

如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。
因为这样的类同时也能存储状态,所以与普通函数相比它们更灵活。例如:
struct absInt
{
	int operator()(int val) const		//返回绝对值
	{
		return val<0 ? -val : val;
	}	
};

int i = -3;
absInt obj;
int ui = obj(i); 	//等价于:obj.operator()(i);  就是函数调用

我们可以调用obj,即使obj只是一个对象而非函数。
***调用对象实际上是在运行重载的调用运算符:本例中,该运算符接受一个int值并返回其绝对值。
函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。

如果类定义了调用运算符,则该类的对象称作函数对象。因为可以调用这种对象,所以说这些对象的行为像函数一样。

含有状态的函数对象类

函数对象类也可以包含其他成员,函数对象类通常含有一些数据成员,这些成员被用于定制调用运算符中的操作。例如:
class PrintString		//打印字符串,并在末尾添加指定字符
{
public:
	PrintString(ostream &o = cout, char c = ' '):os(o),sep(c) {}
	void operator()(const string &s) const {os<<s<<sep;}
private:
	ostream &os;
	char sep;
};

PrintString printer;
printer(s);			//在cout打印s,后面跟一个空格
PrintString errors(cerr,'\n');
errors(s);			//在cerr中打印s,后面跟一个换行符

函数对象常常作为泛型算法的实参。例如,可以使用标准库for_each算法和我们自己的PrintString类来打印容器的内容:
vector<string> vec{ "abc","aoe","qwer" };
for_each(vec.begin(), vec.end(), PrintString(cerr,'\n'));
等价于:第三个参数传递一个函数对象,上面是通过构造函数直接构造,下面是先构造,再传给形参
PrintString print(cerr,'\n');
for_each(vec.begin(), vec.end(), print);

lambda是函数对象

当我们编写一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。
在lambda表达式产生的类中含有一个重载的函数调用运算符:
如上面的例子用lambda重写:
for_each(vec.begin(),vec.end(),[](const string& s){cout<<s<<endl;});
这个lambda生成的未命名的类类似下面:
class NoName
{
public:
	void operator()(const string& s) const
	{cout<<s<<endl;};
};

默认情况下,lambda不能改变捕获的变量,由lambda产生的类当中的函数调用运算符是一个const成员函数,
如果lambda被声明为可变的,则调用运算符就不是const的了
我们也可以用上面的类重写:
for_each(vec.begin(),vec.end(),NoName());

*****表示lambda及相应捕获行为的类*****
当一个lambda表达式通过引用捕获变量时,将由程序负责确保lambda执行时引用所引的对象确实存在。编译器可以直接使用该引用
而无须在lambda产生的类中将其存储为数据成员。
通过值捕获的变量被拷贝到lambda中,这种lambda产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数,令
其使用捕获的变量的值来初始化数据成员。例如:下面的lambda,作用是找到第一个长度不小于给定值的string:

auto wc = find_if(words.begin(),words.end(),[size](const string& a) { return a.size() >= size; });
该lambda表达式产生的类将形如:
class SizeComp
{
public:
	SizeComp(size_t n):size(n){}	//对应的捕获的变量
	bool operator()(const string& s) const	//该调用运算符的返回类型、形参和函数体斗鱼lambda一致
	{ return s.size() >= size; }
private:
	size_t size;	//对应的捕获的变量
};

lambda表达式产生的类不含默认构造函数、赋值运算符及默认析构函数;它是否含有默认的拷贝/移动构造函数则通常要视捕获的
数据成员类型而定。

标准库定义的函数对象
在这里插入图片描述
在算法中使用标准库函数对象
在这里插入图片描述
重点:可调用对象与function
在这里插入图片描述
不同类型可能具有相同的调用形式

学过C#的同学知道,委托和这个功能类似。

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述
自己简单实现类似C#委托的功能:比较简陋,只是提供一个思路

class Action
{
	typedef function<void(int)>  Fun;		//函数类型是无返回值,接受一个int参数
public:
	void operator()(int value)				//重载函数调用运算符
	{
		for (auto fun : vec)
		{
			fun(value);
		}
	}

	void operator+=(Fun fun)				//重载+=运算符
	{
		/*auto iter = find(vec.begin(), vec.end(), fun);	//这里不注释会报错
		if (iter == vec.end())*/
			vec.push_back(fun);
	}
	
	//这里不注释会报错,
	/*void operator-=(Fun fun)				//重载-=运算符
	{		
		auto iter = find(vec.begin(), vec.end(), fun);
		if (iter != vec.end())
			vec.erase(iter);
	}*/
private:
	vector<Fun> vec;
};

void Test(int i)					//一般函数
{
	cout << "Test" << i << endl;
}

struct Test2
{
	void operator()(int i)			//函数对象
	{
		cout << "Test2" << i << endl;
	}
};

void main()
{
	Action myAction;
	myAction += [] (int a) { cout << "lambda" << a << endl; };	//lambda表达式
	myAction += Test;
	myAction += Test2();
	myAction(666);			//利用函数对象执行调用
}

上面会报错的代码的原因:
find会进行==比较两个函数原型void(int),因为没有重载找不到,所以报错。
可以自己重载==操作,但现在还不知道怎么判断两个相同函数原型的函数的相等性

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值