C++知识点之inline函数、回调函数和普通函数

目录
  • 一、inline内联函数#
    • 1.1 使用#
    • 1.2 编译器对 inline 函数处理步骤#
    • 1.3 优缺点#
      • 1.3.1 优点#
      • 1.3.2 慎用内联#
      • 1.3.3 不宜使用内联#
    • 1.4 虚函数(virtual)可以是内联函数(inline)吗?#
  • 二、回调函数和普通函数#
    • 2.1 什么是回调函数?#
    • 2.2 为什么要使用回调函数?#
  • 回调函数和普通函数有什么区别?
  • 总结

一、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 <iostream>
using namespace std;
class Base
{
    public:
    inline virtual void who()
    {
        cout << "I am Base\n";
    }
    virtual ~Base() {}
};

class Derived : public Base
{
    public:
    inline void who() // 不写inline时隐式内联
    {
        cout << "I am Derived\n";
    }
};

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<stdio.h>

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函数、回调函数和普通函数的文章就介绍到这了,更多相关inline函数、回调函数和普通函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++中回调函数(CallBack)的用法分析

    本文实例分析了C++中回调函数(CallBack)的用法.分享给大家供大家参考.具体分析如下: 如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过. 其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即"this"指针,C++通过传递this指针给其成员函数从而实现程序函数可以访问C++的数据成员.这也可以理解为什么C++类的多个实例可以共享成员函数却-有不同的数据成员.由于this指针的作用,使得将一个CALL-BACK型的成员函数作为回调函数安装时

  • C++基础入门教程(九):函数指针之回调

    在Java,要实现某个时间调用某段代码,是很简单的事情,那就是使用接口. 而在C++里,有一个比较高深的方式,那就是使用函数指针. 比如Cocos2d-x的定时器(schedule).消息订阅(NotificationCenter)都使用了函数指针来完成回调的功能. 这也是为什么我们总是能把某个函数作为参数传进去,然后在某个时刻这个函数会被调用. 一.函数的地址 要获取一个int变量的地址很简单,比如int num; 那么num的地址就是&num. 而获取函数的地址更简单,函数的名字就是函数的地

  • c++回调之利用函数指针示例

    c++回调之利用函数指针示例 复制代码 代码如下: #include <iostream>using namespace std; /************************************************************************//*                下层实现: CALLBACK                                        *//**********************************

  • C++函数指针和回调函数使用解析

    函数指针 函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型变.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数.返回值的函数指针变量 实例 以下实例声明了函数指针变量 p,指向函数 max: #include <stdio.h> int max(int x, int y){ return x > y ? x

  • c++中的内联函数inline用法实例

    问题描述:类中成员函数缺省默认是内联的,如果在类定义时就在类内给出函数定义,那当然最好.如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的.内联函数的inline要加在函数前面,不可以加在声明前面. class A { public:void Foo(int x, int y) { } // 自动地成为内联函数 } //正确写法: // 头文件 class A { public: void Foo(int x, int y); } // 定义文

  • C++ inline内联函数详解

    函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码.CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码:被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行. 一个 C/C++ 程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 main().当 main() 调用完了所有的函数,它会返回一个值(例如return 0;)来结束自己的生

  • C++中inline函数详解

    本文主要记录了C++中的inline函数,也就是内联函数,主要记录了以下几个问题: 一.C++为什么引入inline函数? 主要目的:用它代替C语言中表达式形式的宏定义来解决程序中函数调用的效率问题. C语言中的宏定义:#define ExpressionName(var1,var2) (var1+var2)*(var1-var2)这种宏定义,它使用预处理器实现,没有了参数压栈.代码生成等一系列得到操作,因此效率很高.但缺点如下: 仅仅是做预处理器符号表中的简单替换,因此不能进行参数有效性的检测

  • c++函数指针和回调函数示例

    1.函数指针 函数指针是一个指针,只是这个指针它不像普通的指针指向是是一个变量,此时它指向的是一个函数,也就是它存储的是一个函数的地址,如果我们改变它的值,让它所指向的地址由指向funA转变为指向funB,那么这个函数指针的作用就改变了. 2.回调函数 什么是回调函数呢?回调函数其实就是一个通过函数指针调用的函数!假如你把A函数的指针当作参数传给B函数,然后在B函数中通过A函数传进来的这个指针调用A函数,这就是回调机制.B函数就是回调函数. 3.函数指针的使用 3.1函数指针声明 typedef

  • C++ 中回调函数详解及简单实例

    C++ 中回调函数详解及简单实例 回调函数调用关系图 对于第一种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数. 对于第二种情况,主程序可以从执行到回调函数,也可以不执行回调函数,关键看宿主函数是否调用了回调函数.主程序不知道宿主函数什么时候调用回调函数,因为调用函数启动了线程,这样,主程序就不必关心宿主函数什么时候调用回调函数了,主程序可以释放出来做自己的事了. 代码示例 #include <stdio.h> typedef void(*lpFunc

  • c++内联函数(inline)使用详解

    介绍内联函数之前,有必要介绍一下预处理宏.内联函数的功能和预处理宏的功能相似.相信大家都用过预处理宏,我们会经常定义一些宏,如 复制代码 代码如下: #define TABLE_COMP(x) ((x)>0?(x):0) 就定义了一个宏. 为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方.这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行.因此,函数调

随机推荐