zoukankan      html  css  js  c++  java
  • C++对象模型之lambda表达式

    lambda表达式的求值-对象构造

    本来想写“定义”,即“definition”,像函数定义一样,函数具体实现的代码实体即为实现,但是就像lambda既然被称为表达式,它确实有表达式那样“求值”的动作,而不仅仅像函数那样静态地编译。所以应该写“求值”更确切些,即“evaluation”。如果lambda定义的仅仅是一个函数,也就是返回一个函数指针,那么这里就应该叫做“定义”,但是lambda实际上定义了一个“函数对象”,即“function object”。

    首先像下面这样定义一个简单的lambda表达式:

    设好断点,在调试状态下查看相应的汇编代码:

    这是设置为不显示符号名称后更原汁原味的汇编代码:

    可以看到,编译器将a,b的地址压栈,这里都是地址,虽然对于变量a我们是by value而不是by reference。接下来,把lbd的地址装入ecx,然后call,很明显的thiscall,那么lbd自然就是一个object,那么此时此刻被调用的当然就是constructor。

    跟踪到constructor代码处继续看,跳过函数入口的stack frame、寄存器保存等操作,直接定位到目标代码:

    不显示符号名称,对比着看:

    首先,把ecx的值存到堆栈上的this指针,然后通过堆栈上a的地址取到a的值,把a的值存储到this所指对象(即上面的lbd)0偏移处,占4字节(一个int),最后把堆栈上b的地址存储到this所指对象4字节偏移处,占4字节(32位平台指针大小)。一个值、一个地址,在这里我们就可以体会到“by value”和“by reference”的实现原理了。a的值和b的地址在这里就被存储到了lambda对象体内部,对程序员不可见,在对象的生命周期内都不会改变。

    所以我们可以推导出,lbd对象的class定义为:

    class Clambda
    {
    public:
        Clambda(int &a, int &b)
        {
            this->a = a;
            this->pb = &b;
        }
    protected:
        int a;
        int *pb;
    };

    或者写成这样:

    class Clambda
    {
    public:
        Clambda(int &a, int &b) : a(a), b(b)
        {
        }
    protected:
        int a;
        int &b;
    };

    lambda表达式的定义,即表达式的求值,也就是等价类对象的实例化。

     lambda表达式的Function Call Operator

    在原有代码基础之上,加一句调用代码:

    在调试汇编窗口查看:

    不显示符号名称:

    不出所料,在这里我们又看到了thiscall。继续跟踪到函数内部:

    不显示符号名称:

    首先从栈上取到x的值,然后通过this指针取到对象体0偏移处4个字节即变量a的值(“by value”,这个值是lambda对象定义时刻的值),然后相乘,之后通过this指针取到对象体4字节偏移处存储的指向变量b的指针,进一步得到b的值(“by reference”,当前时刻的值),再相乘得到结果。

    现在,我们可以完善lbd的class定义:

    class Clambda
    {
    public:
        Clambda(int &a, int &b)
        {
            this->a = a;
            this->pb = &b;
        }
        int operator()(int x)
        {
            return x * a * *pb;
        }
    protected:
        int a;
        int *pb;
    };

    或者:

    class Clambda
    {
    public:
        Clambda(int &a, int &b) : a(a), b(b)
        {

        }
        int operator()(int x)
        {
            return x * a * b;
        }
    protected:
        int a;
        int &b;
    };

     用class实现等价lambda

    把原来代码中的lambda表达式用我们上面定义的Clambda类替换,汇编代码如下:

    对象构造和function call:

    不显示符号名称:

    构造函数:

    不显示符号名称:

    function call函数体:

    不显示符号名称:

    结束语

    lambda的出现大大方便了function object的创建,编译器帮我们做了多余的工作。然而lambda带来方便的同时,也有很多值得注意的地方,例如笔者在做Win8 Metro开发的时候,就曾不止一次内存访问异常,究其原因就是因为异步lambda按引用捕获的外层函数局部变量已销毁。了解了lambda的底层原理,应该可以帮我们更好的避免此类错误,用好lambda。

    喜爱C++,喜爱lambda。

  • 相关阅读:
    黑盒测试用例输入:等价类划分方法
    jar包/class文件如何快速反编译成java文件
    html表格单元格添加斜下框线的方法
    Linux常用命令操作文档
    压力、负载、性能测试工具总结(持续更新。。。)
    压力测试、负载测试及性能测试异同
    Mac os x安装IDEAL及配置JDK和Maven
    RMQ问题总结,标准RMQ算法的实现
    [c++ IO加速]快速输入输出
    [coj 1353 Guessing the Number]kmp,字符串最小表示法
  • 原文地址:https://www.cnblogs.com/youlin/p/3232290.html
Copyright © 2011-2022 走看看