C++的inline函数、回调函数和普通函数实例分析

本篇内容介绍了“C++的inline函数、回调函数和普通函数实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

成都创新互联是一家专注于成都网站设计、网站制作与策划设计,资兴网站建设哪家好?成都创新互联做网站,专注于网站建设十多年,网设计领域的专业建站公司;建站业务涵盖:资兴等地区。资兴做网站价格咨询:18980820575

    一、inline内联函数#

    特征

    • 相当于把内联函数里面的内容写在调用内联函数处;

    • 相当于不用执行进入函数的步骤,直接执行函数体;

    • 相当于宏,却比宏多了类型检查,真正具有函数特性;

    • 编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数;

    • 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数;

    • 内联关键字是在编译时建议编译器内联,是不是内联函数取决于编译器,一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(是否内联:1、可以通过多次调用函数,查看执行文件大小,如果变大了,就证明是内联函数;2、通过反汇编查看数据)。

    1.1 使用#

    • inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”,也就是说,如果只在生命中使用inline是没有用的,若要成为inline函数必须在定义函数的时候添加该关键字。在声明中加不加inline关键字都没关系,但是为了阅读方便,还是建议声明和定义都加上;

    • C++在类中定义函数的时候,当函数不包含循环、递归、switch 等复杂操作时,编译器会进行隐式内联。

    • C++在类外定义函数,因为与非inline函数不同:inline函数对编译器而言必须是可见的,以便它能够在调用点展开该函数,inline函数必须在调用该函数的每个文本文件中定义。所以内联函数的声明和定义建议都放在同一个头文件,这样另一个.cpp文件#include该头文件的时候,就把该内联函数的定义也包含进来了,这就可以正常使用内联函数了。

    声明

    // 声明1(加 inline,建议使用)
    inline int functionName(int first, int second,...);

    定义

    // 定义
    inline int functionName(int first, int second,...) {/****/};

    类内定义

    // 类内定义,隐式内联
    class A {
    int doA() { return 0; } // 隐式内联
    }

    类外定义

    // 类外定义,需要显式内联
    class A {
    int doA();
    }
    inline int A::doA() { return 0; } // 需要显式内联

    1.2 编译器对 inline 函数处理步骤#

    • 将 inline 函数体复制到 inline 函数调用点处;

    • 为所用 inline 函数中的局部变量分配内存空间;

    • 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中;

    • 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。

    1.3 优缺点#

    1.3.1 优点#

    • 内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。

    • 内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。

    • 在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。

    • 内联函数在运行时可调试,而宏定义不可以。

    1.3.2 慎用内联#

    • 内联是以代码膨胀为代价,仅仅是省去了函数调用的开销,从而提高了函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很小。另一个方面,每一处内联函数调用都要复制代码,将使程序总代码量增大,消耗更多的内存空间。

    • 类的构造函数和析构函数容易让人误解成使用内联函数更有效。要当心构造函数和析构函数可能会隐藏一些行为,如”偷偷地“执行基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类的定义中。

    1.3.3 不宜使用内联#

    • 如果函数体内的代码比较长,使用内联将导致内存消耗代价比较高;

    • 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大;

    1.4 虚函数(virtual)可以是内联函数(inline)吗?#

    • 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。

    • 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。

    • inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 Base::who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。

    如下例程:

    #include 
    using namespace std;
    class Base
    {
        public:
        inline virtual void who()
        {
            cout << "I am Base
    ";
        }
        virtual ~Base() {}
    };
        
    class Derived : public Base
    {
        public:
        inline void who() // 不写inline时隐式内联
        {
            cout << "I am Derived
    ";
        }
    };
    
    int main()
    {
    // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。
    Base b;
    b.who();
    
    // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。
    Base *ptr = new Derived();
    ptr->who();
    
    // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。
    delete ptr;
    ptr = nullptr;
    
    system("pause");
    return 0;
    }

    二、回调函数和普通函数#

    更详细的回调函数理解可以查看本地的这个文章【【知识点】10张图让你彻底理解回调函数】

    2.1 什么是回调函数?#

    把a函数指针像参数传递那样传给b函数,而这个a函数会在某个时刻被b函数调用执行,这就叫做回调,a函数称为回调函数。如果回调函数立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。

    2.2 为什么要使用回调函数?#

    先抛出答案:回调函数的好处和作用,那就是解耦,对,就是这么简单的答案,就是因为这个特点,普通函数代替不了回调函数。

    如下代码:

    int Callback_1()
    {
        printf("Hello");
        printf("This is Callback_1 "); 
        return 0;
    }
    
    int Callback_2() 
    {
        printf("Hello");
        printf("This is Callback_2 ");    
        return 0;
    }

    发现以上代码是可以解耦的,因为两个函数都执行了printf("Hello"),这个时候我们可以通过回调的方式进行解耦,如下:

    #include
    
    int Callback_1()    // Callback Function 1
    {
        printf("This is Callback_1 "); 
        return 0;
    }
    
    int Callback_2()    // Callback Function 2
    {    
        printf("This is Callback_2 ");    
        return 0;
    }
    
    int Handle(int (*Callback)())
    {    
        printf("Entering Handle Function. ");    
        Callback();    
        printf("Leaving Handle Function. ");
    }
    
    int main()
    {    
        printf("Entering Main Function. ");    
        Handle(Callback_1);    
        Handle(Callback_2);  
        printf("Leaving Main Function. ");    
        return 0;
    }

    像这样我们就减少了重复代码啦,也就是解耦。这是使用普通函数调用无法做到的。

    回调函数和普通函数有什么区别?

    1、对普通函数的调用:调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返回调用程序继续执行。从发出调用的程序的角度看,这个过程为“调用-->等待被调用函数执行完毕-->继续执行”。

    2、对回调函数调用:调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来。

    “C++的inline函数、回调函数和普通函数实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!


    网站名称:C++的inline函数、回调函数和普通函数实例分析
    标题网址:http://ybzwz.com/article/pjscgc.html