目录
01.模板类 //有的地方叫函数模板类,没必要啊,容易引起歧义
00.背景
笔者在使用C++开发的时候产生过一个想法:能不能获得成员函数的地址,转为函数指针进行操作呢?
尝试1:获取静态成员函数的首地址
class Math
{
public:
static int min(int a, int b){
return a>=b ? b : a;
}
};
int main()
{
auto fPtr = &Math::min; //企图获取一个函数指针
cout << (*fPtr)(10, 20) << endl; //ok! 这么写容易暴露了C语言的出身,哈哈
return 0;
}
尝试2:获取访问内存的普通成员函数的地址
class Math
{
public:
int min(){
return num1>=num2 ? num2 : num1;
}
int num1 {10};
int num2 {20};
};
int main()
{
auto fPtr = &Math::min; //error! 企图获取一个函数指针
Math *objPtr = nullptr;
auto a = objPtr->min; //error! 企图获取一个空对象的方法
cout << (*a)() << endl; //error! 语法糖! 当心存敬畏,勿用,无用,忘掉它!
return 0;
}
其实,上面2次尝试有点不讲武德,没准备好构造函数就蛮干!只要类封装了数据,上面的尝试注定失败;
类是数据和方法的封装集合,脱离数据使用方法就应该使用自由函数!而不是成员函数!
那么正确的获取'某个实例的成员函数de地址并在适当的时候调用'应该怎么写呢?//为什么有这种需求?这个要从闭包说起
01.模板类 //有的地方叫函数模板类,没必要啊,容易引起歧义
声明形式:
template <class T> function; // undefined
template <class Ret, class... Args> class function<Ret(Args...)>; //看不懂没关系,看实例
定义:std::function是一个封装了特定方法和数据的'模板类',用于记录运行时实例的状态及函数;
作用:
1> 形成闭包;//什么是闭包,我已经透露了!
2> 冒充函数指针欺骗小白;//简言之,不该用的时候用,就是装逼!
02.包装普通函数//静态自由函数是同理的
void normalFunc(int a)
{
cout << "normalFunc()" << a << endl;
return;
}
int main()
{
std::finction<void (int)> fun1 = normalFunc;
fun1(10);
return 0;
}
03.包装静态成员函数
class Math
{
public:
static int min(){
return num1>=num2 ? num2 : num1;
}
static int num1;
static int num2;
};
int Math::num1 = 5; //静态成员变量的类外初始化
int Math::num2 = 10; //静态成员变量的类外初始化
int main()
{
std::function<int ()> obj = &Math::min; //企图获取一个函数指针
cout << obj() << endl; //ok! 再不要写(*0bj)(),obj是一个模板类实例,加*号是说不通的
return 0;
}
04.包装实例的无参成员函数
class Math
{
public:
Math(int a, int b): num1(a), num2(b){}
int min()
{
return num1>=num2 ? num2 : num1;
}
int num1;
int num2;
};
int main()
{
Math m(10, 20);
//std::function<int()> f = &m.min; //error! 只获取实例的方法不被允许,尽管意义上说得通
//std::function<int()> f = &Math::min; //error! 针对那个实例没有做说明
std::function<int()> f = bind(&Math::min, &m);
cout << f() << endl; //error! 非法访问内存
return 0;
}
05.包装实例的'有参成员函数'
class Math
{
public:
int min(int a, int b)
{
return a>=b ? b : a;
}
};
int main()
{
Math m;
//std::function<int(int, int)> f = &m.min; //error! 只获取实例的方法不被允许,尽管意义上说得通
//std::function<int(int, int)> f = &Math::min; //error! 针对那个实例没有做说明
std::function<int(int, int)> f = bind(&Math::min, &m, placeholders::_1, placeholders::_2);
cout << f(10, 20) << endl; //error! 非法访问内存
return 0;
}
06.包装lambda函数 //最常用
class Math
{
public:
int min(int a, int b)
{
return a>=b ? b : a;
}
};
int main()
{
Math m;
//std::function<int(int, int)> f = &m.min; //error! 只获取实例的方法不被允许,尽管意义上说得通
//std::function<int(int, int)> f = &Math::min; //error! 针对哪个实例没有作说明
std::function<int(int, int)> f = [&](int x, int y)->int{ return m.min(x, y);}; //只能用捕获引用
cout << f(10, 20) << endl; //error! 非法访问内存
return 0;
}
07.总结
前面的示例还好理解,但是到了04、05、06就有点费劲了,为什么呢?这里再强调几点:
1)单独获取类的成员函数而不获取成员变量,这种操作违背了C++的封装思想,除非这个成员函数是static的;
2)单独获取一个实例的成员函数无论如何是不能通过的,要获取就要带捕获成员变量的方式一同获取;
3)关于函数std::bind()和lambda函数需要先去学习后再来搞懂,才能真正理解这3者的关系;
4)模板类std::function()还可以包装函数对象、模板函数,但本质都是一样的。//封装数据及方法到一个实例