前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++の函数——内联函数&函数指针

C++の函数——内联函数&函数指针

作者头像
leoay
发布2020-01-14 16:27:59
2.5K0
发布2020-01-14 16:27:59
举报
文章被收录于专栏:leoay

C++の函数

—— 内联函数&函数指针

今天我们继续讨论C++函数部分,剩下两个点,一个是内联函数,另一个是函数指针

内联函数

我们先看一下内联函数。内联函数也是C++中的一个重要特性。所谓内联函数,其实本质上也是一种函数,在形式上的表现就是在普通函数前面加上关键字"inline",然后相对于普通函数来说,它也比较短小。C++中"inline"的作用其实是为了优化代码的运行,降低代码的执行时间,就像在C语言中的宏函数一样,作用也是为了降低代码的执行时间。

当内联函数被调用时,并不会向普通函数一样从主函数跳转到函数,而是直接将内联函数中的代码逻辑替换进主函数,提高运行效率。而这个过程是在代码编译的过程即完成的,当我们将一个函数定义为内联函数时,编译器识别到内联函数的特征后,就将函数的定义替换到函数的调用。那么我们怎么定义内联函数呢?

如何定义内联函数

定义内联函数就要在函数的前面使用“inline”关键字。像下面这样:

代码语言:javascript
复制
inline int add(int a, int b);     //(1)
inline int add(int a, int b)      //(2)
{
    return (a + b);
}
void test()                 //test func
{
    int i = 4;
    int j = 6;
    add(i, j);
}
void test_f()             //equal to test()
{
    int i = 4;
    int j = 6;
    (i + j);
}

我们看到上面有两个部分,一个是add函数的声明,一个是add函数的定义,并且每个函数前都有“inline”,我们便将“add”函数定义为内联函数,那么在代码中调用时就是将add函数的定义替换为调用部分的代码,如上面的test(),在编译的时候就会自动转为test_f()。

注意:有一点需要注意,并不是每一个用inline标明的函数都会被编译器转为内联,内联的根本目的是优化程序的运行,因此对于使用较为频繁的短小的函数,才有明显的效果,如果函数较为庞大,编译器也会忽略掉函数前面的“inline”将其变为普通函数。

为什么要用内联函数

我们在代码中经常会用到一些小函数,它们逻辑简单,代码量少,但是如果考虑到这些函数被调用者调用的时候,我们会发现大部分的时间都耗费在调用这个过程,也就是程序从主函数跳转到被调用函数的过程,而不是在我们写的这个小函数中。实际上正常的函数调用指令时,程序立即在函数调用语句之后存储指令的内存地址,将被调用的函数加载到内存中复制参数值,跳转到被调用函数的内存位置,执行函数代码,存储函数的返回值,然后跳转回执行被调用函数之前保存的指令地址。这样会导致程序的运行时间开销太大。

而C++的内联函数则提供了一种替代的方法,使用inline关键字,编译器用函数代码本身替换函数调用语句,然后再编译整个代码。因此,对于内联函数,编译器不必跳到另一个位置去执行函数,然后再跳回去,因为被调用程序的代码已经是调用程序中代码的一部分了。

下面我们列举一下内联函数的优缺点:

优点:

1、内联函数通过避免函数调用开销从而加速了我们的程序

2、当函数调用发生时,内联函数节省了堆栈上变量push/pop的开销

3、内联函数节省了从函数返回调用开销

4、内联函数通过使用指令缓存来增加引用的局部性

5、通过将其标记为内联,您可以将函数定义放入头文件中

缺点:

1、由于代码扩展,它增加了可执行文件的大小

2、c++内联在编译时解决。这意味着如果您更改内联函数的代码,您将需要使用它重新编译所有代码,以确保它将被更新

3、当在头文件中使用时,它会使头文件变大,包含用户不关心的信息

4、如上所述,它增加了可执行文件的大小,这可能会导致内存抖动。更多的页面错误会降低程序性能

5、有时并不有用,例如在嵌入式系统中,由于内存限制,大的可执行文件大小根本不是首选

什么时候使用内联函数

函数可以根据程序员的需要进行内嵌,那么我们什么时候使用呢?

1、当性能优先时,应该使用内联函数

2、在宏上使用内嵌函数

3、优先在函数定义中使用类外的inline关键字来隐藏实现细节

函数指针

所谓函数指针,其实本质上还是指针,但是不同于我们之前提到的指针,函数指针是指向函数的指针。根据前面的文章,我们很容易声明一个函数,如下我们声明一个比较两个字符串长度的函数:

代码语言:javascript
复制
bool lengthCompare(const string &, const string &);

现在,我们再来看一下函数指针的声明方式吧:

代码语言:javascript
复制
bool (*pf)(const string &, const string &);

从上面我们可以看到pf前面有个*,因此pf是一个指针,右面是形参列表,所以pf是指向函数的指针,从前面的bool可以看出这个函数的返回值类型是bool类型。

注意: *pf两边的()是必须的,因为这代表*pf是一个整体,pf是一个指针,如果不加括号,就表示bool* 是一个整体,pf就成了函数名,那么它的含义就变成了返回值为bool类型指针的函数了,这样是不是很好理解?

如何使用函数指针

其实同数组一样,函数名就代表了函数入口的首地址,也就是我们说的函数指针。对于上面两个例子来说,由于他们具有相同的参数列表,因此我们可以得到下面的等价式:

代码语言:javascript
复制
pf = lengthCompare;
pf = &lengthCompare;

可以看到,函数名就是函数的首地址,也表示函数本身。

因此,我们也会有下面的调用方式:

代码语言:javascript
复制
bool b1 = pf("leoay", "learn C++");
bool b2 = (*pf)("leoay", "learn C++!");
bool b3 = lengthCompare("leoay", "learn C++!!");

可以看到,我们并不需要对函数指针进行解引用就能直接调用它,因为我们在调用函数的时候其实就是找函数在程序中的首地址,然后将参数传进去。

重载函数的指针

前面我们说到了函数的重载,就是说在同一个源文件中函数具有相同的名字,但是具有不同的参数列表时的情况,因此我们很容易延伸到函数指针里面,就是这里要说的重载函数指针。我们先来看一下怎么声明重载函数指针:

代码语言:javascript
复制
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned  int) = ff;

从上面的代码,我们可以看出想要使用重载函数指针,我们就要先声明重载函数,然后我们在定义一个函数指针时,将重载函数的地址赋值给这个函数指针,这里有一点我们需要注意,既然重载函数有不同的列表,那么我们在定义重载函数指针时该怎么选择呢?当然是与我们想要使用的那个重载函数保持一致。就是说我们想用哪个重载函数定义函数指针,函数指针的参数列表就应该与哪个重载函数保持一致。

把函数指针当做参数

到这里,我们发现函数指针并没有什么神奇的地方,我们完全可以把它当做一个指针看待,只不过具备函数的一些特征。但是,回归根本,它还是一枚可爱的指针。因此,它应该具备指针的一些特征。比如,我们可以把它当做参数传递给其他的参数。以后我们会讲到,C++中常见的回调函数就是这样使用的。

下面我们就用代码演示一下这种骚操作吧:

代码语言:javascript
复制
void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
void useBigger(const string &s1, const string &s2, bool (*pf)(const string &, const string &));

可以看到上面的代码中两个函数的参数中分别有下面这两个参数:

代码语言:javascript
复制
bool pf(const string &, const string &)
bool (*pf)(const string &, const string &)

上面是一个函数,下面是一个函数指针。但是在这里实际上他们是等价的,当函数被作为参数传递给另一个参数的时候,是等价于函数指针的。所以上面两个声明其实是等价的。

对于函数指针与内联函数的说明这篇文章就到这里,由于是基础系列文章,先不详细展开,还有一些知识需要用实例和练习加以说明。以后如果出高级系列再详细展开讨论。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 leoay 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档