91 CPP11神器之可调用对象

C++11引入了智能指针、移动语义和可调用对象的概念,如std::function和std::bind。这些特性使得管理内存、处理函数和参数绑定更为便捷。文章详细阐述了普通函数、类的静态成员函数、仿函数、lambda函数以及类的非静态成员函数作为可调用对象的使用,并探讨了它们在回调函数和替代虚函数中的应用。
摘要由CSDN通过智能技术生成

C++11新增了很多东西,只有三个可以称为神器:

1 智能指针

2 移动语义和

3 可调用对象的包装器和绑定器

下面详解可调用对象、包装器std::function、绑定器std::bind

以及它们最有价值的应用场景:

可变函数和参数、回调函数、取代虚函数

可调用对象:

在C++中,可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象。

可调用对象有类型,可以用指针存储它们的地址,可以被引用(类的成员函数除外)

一、普通函数

普通函数类型可以声明函数、定义函数指针和函数引用,但是不能定义函数的实体。

#include <iostream>
using namespace std;

using Fun = void(int, const string &); //普通函数类型的别名

Fun show;                              //声明普通函数,这一行效果等同于下面这一行
// void show(int, const string &);//声明普通函数

void test()
{
    show(1, "a1"); //直接调用普通函数

    // 1 C风格的

    void (*pf)(int, const string &) = show; //声明函数指针,指向普通函数
    pf(2, "a2");
    void (&rf)(int, const string &) = show; //声明函数引用,引用普通函数
    rf(3, "a3");

    // 2 C++风格的

    //声明函数指针,指向普通函数
    Fun *pf2 = show;
    pf2(4, "a4");

    //声明函数引用,引用普通函数
    Fun &rf2 = show;
    rf2(5, "a5");
}
int main()
{
    test();
    return 0;
}
//定义普通函数
void show(int a, const string &b)
{
    cout << a << " " << b << endl;
}

//以下代码是错误的,不能用函数类型定义函数的实体.
// Fun show(int a, const string &b)
// {
//     cout << a << " " << b << endl;
// }
/*1 a1
2 a2
3 a3
4 a4
5 a5 */

函数指针我们经常使用,很少使用函数引用,引用的本质是指针,所以,函数指针和函数引用是同一个东西

上面的demo重点表达函数类型和函数对象这两个概念。

在C++中,函数是一种数据类型,函数的实体被看成对象,而不只是一段代码。

函数是对象,可以调用的对象,或者叫函数对象,这种思路和传统的不一样

二、类的静态成员函数

类的静态成员函数和普通函数本质上是一样的,把普通函数放在类中而已

#include <iostream>
using namespace std;

using Fun = void(int, const string &); //别名

struct AA
{
    static void show(int a, const string &b)
    {
        cout << a << " " << b << endl;
    }
};

void test()
{
    AA::show(1, "a1"); //直接调用静态成员函数

    // 1 C风格的

    void (*pf)(int, const string &) = AA::show; //声明函数指针
    pf(2, "a2");
    void (&rf)(int, const string &) = AA::show; //声明函数引用
    rf(3, "a3");

    // 2 C++风格的

    //声明函数指针
    Fun *pf2 = AA::show;
    pf2(4, "a4");

    //声明函数引用
    Fun &rf2 = AA::show;
    rf2(5, "a5");
}
int main()
{
    test();
    return 0;
}

三、仿函数

仿函数的本质是类,调用的代码像函数。

仿函数的类型就是类的类型。

普通类创建的对象为什么不是可调用的对象呢?

因为它不能像函数那样直接调用,就扣字面意思,调用它的成员函数是另外一回事。

#include <iostream>
using namespace std;
struct AA
{
    void operator()(int a, const string &b)
    {
        cout << a << " " << b << endl;
    }
};

void test()
{
    AA aa;
    aa(1, "1"); //用对象调用仿函数

    AA()
    (2, "2"); //用匿名对象调用仿函数,这里换行是VSCODE格式化就这样,不报错

    AA &raa = aa; //引用函数
    raa(3, "3");  //用对象的引用调用仿函数
}
int main()
{
    test();
    return 0;
}

仿函数是类,当然可以创建引用

四、lambda函数

lambda函数的本质是仿函数,仿函数的本质是类

#include <iostream>
using namespace std;

void test()
{
    auto func = [](int a, const string &b)
    {
        cout << a << " " << b << endl;
    };
    func(1, "a"); //用lambda对象调用仿函数

    //引用lambda
    auto &func2 = func;
    func2(2, "b"); //引用lambda对象的引用调用仿函数
}
int main()
{
    test();
    return 0;
}

五、类的非静态成员函数 (这个与众不同,所以需要包装器和绑定器)

类的非静态成员函数有地址,但是,只能通过类的对象 才能调用它,所以C++对它做了特别处理。

类的非静态成员函数只有指针类型,没有引用类型,不能引用。

C++有6种可调用对象,只有类的非静态成员函数与众不同,正因为它的不同,所以才需要可调用对象的包装器和绑定器。但是它确实最重要的可调用对象,不能没有它。

#include <iostream>
using namespace std;

//普通函数
void show(int a, const string &b)
{
    cout << a << " " << b << endl;
}

struct AA
{
    void show(int a, const string &b)
    {
        cout << a << " " << b << endl;
    }
};
void test()
{
    AA aa;
    aa.show(1, "a1");

    // 1 传统C风格
    void (*pft1)(int, const string &) = show;  //普通函数指针,普通函数可以省略&,也可以不省略
    void (*pft2)(int, const string &) = &show; //普通函数指针,普通函数可以省略&,也可以不省略

    void (AA::*pf)(int, const string &) = &AA::show; //定义类的成员函数的指针,&AA::show,取地址的符号&不能省略
    (aa.*pf)(2, "a2");                               //用类的成员函数的指针调用成员函数

    // 2 C++11风格

    //类成员函数的指针类型  起别名
    using pFun = void (AA::*)(int, const string &);
    pFun pf1 = &AA::show; //让类成员函数的指针指向类的成员函数的地址
    (aa.*pf1)(11, "aa1"); //用类成员函数的指针调用类的成员函数
}
int main()
{
    test();
    return 0;
}

这种调用的方式与其它可调用的对象不一样,其它可调用对象用变量名(对象名)就可以直接调用,而类成员函数这种可调用对象,需要对象名和函数指针变量名组合在一起才能调用。

这种区别给模板带来了麻烦,代码不能统一,模板没法写。

怎么解决这个问题,后面包装器绑定器再说。

六、可被转换为函数指针的类对象 (意义不大)

类可以重载类型转换运算符

语法:

operator 数据类型()

如果数据类型时函数指针或者函数引用类型,那么该类实例也将成为可调用对象。

它的本质是类,调用的代码像函数。

为什么说这种方式没什么意义呢?

因为这个转换函数只能返回普通全局函数和类的静态成员函数,不能返回类的非静态成员函数,普通全局函数和类的静态成员函数本身就是可调用对象,直接调用就行了,用不着用这个,没任何好处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值