函数参数
在大多数情况下,函数参数是按值传递的。这意味着参数的值被复制到一个局部变量中。对于int、float、double、bool、enum 以及指针和引用等简单类型,这非常快。
数组总是使用指针传递,除非它们被打包在类或者结构体中。
如果参数是复合类型,在以下情况下传值更高效,否则使用指针和引用更高效
- 对象很小,可以装入一个寄存器
- 对象没有拷贝构造函数和析构函数
- 对象没有虚成员
- 对象没有使用RTTI
将复合对象传递给函数的首选方法是使用const引用。其次是使函数成为对象的类成员
64位unix系统允许寄存器中传输最多14个参数(8个float或double, 加上6个整数、指针或引用参数)
函数返回类型
函数的返回类型最好是简单类型、指针、引用或 void。返回复合类型的对象更为复杂,而且常常效率低下。
简单情况下,复合类型对象直接从寄存器返回。否则通过一个隐藏指针将它们复制到调用方指定的位置。
当直接返回复杂类型对象的值时,编译器可能会进行RVO(return value optimization)优化,从而避免复制构造和析构成本,但开发者不应依赖这一点。
函数尾调用
尾调用是优化函数调用的一种方法。如果函数的最后一条语句是对另一个函数的调用,那么编译器可以用跳转到第二个函数 来替换该调用。 优化编译器将自动完成此任务。第二个函数不会返回到第一个函数,而是直接返回第一个函数被调用的位 置。这样效率更高,因为它消除了返回操作。
递归函数
函数递归调用对于处理递归数据结构非常有用。递归函数的代价是所有参数和局部变量在每次递归时都会有一个新实例,这会占用栈空间。
较宽的树形结构比较深的树形结构,有更高的递归效率 .
无分支递归总是可以用循环代替,这样的效率更高
结构体和类
面向对象的好处
- 变量存储在一起,数据缓存更有效率
- 无需将类成员变量作为参数传递给类成员函数,避免参数传递的开销
面向对象的坏处:
- 非静态成员函数有this指针,有额外开销
- 虚成员函数的效率较低
如果面向对象的编程风格有利于程序的逻辑结构和清晰性,那么你可以使用这种风格
类的数据成员
类或结构体的数据成员是按创建类或结构实例时声明它们的顺序连续存储。将数据组织到类或结构体中不存在性能损失。
大多数编译器将数据成员对齐到可以被特定数整除的地址以优化访问,副作用是产生字节空洞
类的成员函数
每次声明或创建类的新对象时,它都会生成数据成员的新实例。但是每个成员函数只有一个实例。函数代码不会被复制
静态成员函数不能访问任何非静态数据成员或非静态成员函数。静态成员函数比非静态成员函数快。
虚成员函数
多态性是面向对象程序比非面向对象程序效率低的主要原因之一。 如果可以避免使用虚函数,那么你就可以获得面向对象编程的大多数优势,而无需付出性能成本 。
如果函数调用语句总是调用虚函数的相同版本,那么调用虚成员函数所花费的时间要比调用非虚成员函数多几个时钟周期。 如果版本发生了变化,你可能会得到10 ‐ 20个时钟周期的错误预测惩罚。
有时可以使用模板而不是虚函数来获得所需的多态性效果。
运行时类型识别(RTTI)
效率不高。如果编译器有RTTI 选项,那么关闭它并使用其他实现。
继承
派生类的对象与包含父类和子类成员的简单类的对象的实现方法相同。父类和子类的成员访问速度相同。一般来说,你可以 假设使用继承几乎没有任何性能损失。
除了
- 父类数据成员大小会添加到子类成员的偏移量中。偏移量太大时,会造成数据缓存效率降低
- 父类和子类代码可能在不同模块。造成代码缓存效率降低
尽量不使用多重继承,代之以组合
联合体
union 是数据成员共享相同内存空间的结构。union 可以通过允许从不同时使用的两个数据成员共享同一块内存来节省内存 空间。
位域
位域可能有助于使数据更加紧凑。访问位域成员不如访问结构的成员效率高。如果在大数组可以节省缓存空间或使文件更 小,那么额外的时间是合理的
重载函数
重载函数的不同版本被简单地视为不同的函数。使用重载函数没有性能损失
重载运算符
重载的运算符相当于一个函数。使用重载运算符与使用具有相同功能的函数效率一样