专栏首页C/C++基础Google C++编程风格指南(二)之函数的相关规范

Google C++编程风格指南(二)之函数的相关规范

1.内联函数的使用规范

定义:内联函数是指用inline关键字修饰的函数。在类内定义的函数被默认成内联函数。

特点:是编译器可能会将其内联展开,编译时,类似于宏替换,使用函数体替换调用处的函数名,以减少函数调用的开销,无需按通常的函数调用机制调用内联函数。

优点:当函数体比较小的时候,内联该函数可以令目标代码更加高效。

缺点:滥用内联将导致程序变慢,内联有可能使目标代码量增加或减,返取决于被内联的函数的大小。内联较短小的存取函数通常会减少代码量,但内联一个较大的函数(注:如果编译器允许的话)将显著增加代码量。在现代处理器上,由亍更好的利用指令缓存(instruction cache),小巧的代码往往执行更快。

使用inline函数应该遵循以下几点: (1)内联函数最好不要超过10行;

(2)对于析构函数应慎重对待,析构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被调用;

(3)递归函数不应该被声明为内联函数。原因是递归调用堆栈的展开并不像循环那么简单,比如递归次数在编译时可能是未知的,大多数编译器都不支持内联递归函数。

考察如下代码,一个简单的递归调用:

#include <iostream>
using namespace std;

inline void print(int n){
    --n;
    if(n>0)
        print(n);
    else 
        for(int i=0;i<5;++i)
            cout<<"this is inline function:"<<i<<endl;

}

int main(){
    print(10);
}

在VS2012中转到反汇编,其汇编代码为:

int main(){
002E12B0  push        ebp  
002E12B1  mov         ebp,esp  
002E12B3  and         esp,0FFFFFFF8h  
    print(10);
002E12B6  call        print (02E1270h)  
}

可见,即使将递归函数print(int n)定义为inline函数,实际上编译器并未将其内联展开,按照正常的函数去调用它。

(4)虚函数不应该被申明为内联函数。因为虚函数的调用较普通函数复杂,需要运行时通过查找虚函数表动态获取虚函数的入口地址,编译器编译阶段是不能确定虚函数的入口地址,故不能将其在编译时静态展开。

(5)如果对析构函数内联,主要原因是在类体重定义,为了方便抑或是其他原因,应对其行为给出文档说明。

2.函数相关规范

2.1函数参数顺序(Function Parameter Ordering)

定义函数时,参数顺序为:输入参数在前,输出参数在后。

C/C++函数参数分为输入参数和输出参数两种,有时输入参数也会输出(注:值被修改时)。输入参数一般传值或常数引用(const references),输出参数或输入/输出参数为非常数指针(non-const pointers)。对参数排序时,将所有输入参数置于输出参数之前。不要仅仅因为新添加的参数,就将其置于最后,而应该依然置于输出参数之前。

注意,返一点并不是必须遵循的规则,输入/输出两用参数(通常是类/结极体发量)混在其中,会使得规则难以遵循。

2.2不要设计多用途面面俱到的函数

多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。 应编写功能单一集中的函数。

2.3函数的规模

函数的规模尽量限制在80行以内 ,不包括注释和空格行。其次,避免设计多参数函数,不使用的参数从接口中去掉,其目的是为了减小函数接口的复杂度。

2.4尽量编写线程安全函数与可重入函数

2.4.1什么是线程安全函数

线程安全函数是多线程情况下,可安全的被多个线程并发执行的函数。

确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器。因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。

线程安全函数与线程不安全函数示例:

static int tmp;  
//线程不安全函数
void func1(int* x, int* y) {  
     tmp=*x;  
     *x=*y;  
     *y=tmp;  
} 

//线程安全函数
void func2(int* x, int* y) {  
     int tmp;  
     tmp=*x;  
     *x=*y;  
     *y=tmp;  

}  

func1是线程不安全函数,因为func1在被多线程并发调用时,使用的共享变量tmp可能被其它线程的func1改变,从而导致函数结果的不确定性。解决办法就是给全局变量tmp加锁,或者使用私有局部变量,函数func2就这这样做的。

2.4.2什么是可重入函数

可重入函数的定义本人目前没有找到比较权威的参考资料,个人理解是:可以被中断处理函数调用的线程安全函数是可重入函数。关于可重入的概念,可以参考可重入.维基百科

也就是说,可重入函数必定可以被安全的并发执行。安全指函数的运行结果必须满足预期,不存在不确定性。

对于百度百科的描述,实际上是介绍了一个特殊的场景下,满足这个场景的线程安全函数就是可重入函数。这个特殊的场景就是函数在响应中断期间,被中断处理函数再次调用,这就是“重入”,重新进入的形象描述。再次被调用可以安全的进行,这就是“可重入”。相反, 不可重入(non-reentrant)不可重入的后果主要体现在象终端处理函数需要重入的情况中,如果信号处理函数中使用了不可重入的函数,则可能导致程序的错误甚至崩溃。

要确保函数可重入,需满足以下几个条件: (1)不在函数内部使用静态或全局数据; (2)不返回静态或全局数据,所有数据都由函数的调用者提供; (3)使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据; (4)不调用不可重入函数。

2.4.3可重入函数与线程安全函数的区别

(1)关系 可重入函数是线程安全函数的子集,即可重入函数一定是线程安全函数,线程安全函数不一定是可重入函数。

关系图如下:

(2)区别 在线程安全函数可以对共享地址空间数据加锁,可重入函数则不能。因为在可重入函数响应中断时,中断处理函数若再次调用该函数时,会发生死锁。

其它的区别,本人暂未发现,理解到,若有人知晓,希望勿吝赐教,留言告知。

在多线程条件下,应当做到函数是线程安全的,更进一步,做到可重入 。


参考文献

[1]百度百科.可重入函数 [2]百度文库.Google C++编码规范中文版

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C/C++编码规范

    对于变成人员,良好的编程风格是提高程序可靠性和效率非常重要的手段。而编码规范就是对编程风格最好的约束保障。 严格遵守编码规范方便代码的交流和维护,利于提高代...

    Dabelv
  • C++中函数重载、隐藏、覆盖和重写的区别

    C++规定在同一作用域中,同名函数的形式参数(指参数的个数、类型或者顺序)不同时,构成函数重载。

    Dabelv
  • 动态联编实现原理分析

    所谓动态联编,是指被调函数入口地址是在运行时、而不是在编译时决定的。C++语言利用动态联编来完成虚函数调用。C++标准并没有规定如何实现动态联编,但大多数的C+...

    Dabelv
  • 浅谈javascript中的回调函数javascript中的函数匿名函数回调函数回调函数的使用回调函数实例总结

    要理解javascript中的回调函数,首先我们就要对javascript中的函数有一定的理解,所以我们先从javascript中函数谈起,讲讲它与其他语言中的...

    desperate633
  • 函数式编程与面向对象编程[1]: Lambda表达式 函数柯里化 高阶函数函数式编程与面向对象编程[1]: Lambda表达式 函数柯里化 高阶函数.md

    For example, in Lisp the 'square' function can be expressed as a lambda expressi...

    一个会写诗的程序员
  • 码如其人,小老弟,你能写一手漂亮的Python函数吗

    好的 Python 函数与蹩脚 Python 函数的区别是什么?「好」函数的定义之多让人惊讶。从我们的目的出发,我会把好的 Python 函数定义为符合以下清单...

    一墨编程学习
  • 写 Python 代码不可不知的函数式编程技术

    近来,越来越多人使用函数式编程(functional programming)。因此,很多传统的命令式语言(如 Java 和 Python)开始支持函数式编程技...

    OpenCV学堂
  • python语法之函数

    函数:   将特定功能代码编写在一个函数里   便于阅读和复用   对一组表达特定功能表达式的封装   使程序模块化 python内置函数:   ...

    py3study
  • Jmeter(二十四) - 从入门到精通 - JMeter函数 - 中篇(详解教程)

      在性能测试中为了真实模拟用户请求,往往我们需要让提交的表单内容每次都发生变化,这个过程叫做参数化。JMeter配置元件与前置处理器都能帮助我们进行参数化,但...

    北京-宏哥
  • python高阶函数

    高阶函数定义: 1.函数接收的参数是一个函数名; 2.函数返回的是一个函数名; 只要满足上述条件中的任意一个条件的函数均属于高阶函数

    py3study

扫码关注云+社区

领取腾讯云代金券