相对来说,C语言是一种简洁的语言,所涉及的概念和元素比较少,主要是:宏(macro)、指针(pointer)、结构(struct)、函数(function)和数组(array),比较容易掌握和理解。而C++(www.cppentry.com)不仅包含了上面所提到的元素,还提供了私有成员(private members)、公有成员(public members)、函数重载(function overloading)、缺省参数(default parameters)、构造函数、析构函数、对象的引用(references)、操作符重载(operator overloading)、友元(friends)、模板(templates)、异常处理(exceptions)等诸多的要素,给程序员提供了更大的设计空间,同时也增加了软件设计的难度。
C语言之所以能被广泛的应用,其高效率是一个不可忽略的原因,C语言的效率能达到汇编语言的80%以上,对于一种高级语言来说,C语言的高效率就不言而喻了。那么,C++(www.cppentry.com)相对于C来说,其效率如何呢?实际上,C++(www.cppentry.com)的设计者stroustrup要求C++(www.cppentry.com)效率必须至少维持在与C相差5%以内,所以,经过精心设计和实现的C++(www.cppentry.com)同样有很高的效率,但并非所有C++(www.cppentry.com)程序具有当然的高效率,由于C++(www.cppentry.com)的特殊性,一些不好的设计和实现习惯依然会对系统的效率造成较大的影响。同时,也由于有一部分程序员对C++(www.cppentry.com)的一些底层实现机制不够了解,就不能从原理上理解如何提高软件系统的效率。
本文主要讨论两个方面的问题:第一,对比C++(www.cppentry.com)的函数调用和C函数调用,解析C++(www.cppentry.com)的函数调用机制;第二,例举一些C++(www.cppentry.com)程序员不太注意的技术细节,解释如何提高C++(www.cppentry.com)的效率。为方便起见,本文的讨论以下面所描述的单一继承为例(多重继承有其特殊性,另作讨论)。
class X
{
public:
virtual ~X(); //析构函数
virtual void VirtualFunc(); //虚函数
inline int InlineFunc() { return m_iMember}; //内联函数
void NormalFunc(); //普通成员函数
static void StaticFunc(); //静态函数
private:
int m_iMember;
};
class XX: public X
{
public:
XX();
virtual ~XX();
virtual void VirtualFunc();
private:
String m_strName;
int m_iMember2;
};
C++(www.cppentry.com)的的函数分为四种:内联函数(inline member function)、静态成员函数(static member function)、虚函数(virtual member function)和普通成员函数。
内联函数类似于C语言中的宏定义函数调用,C++(www.cppentry.com)编译器将内联函数的函数体扩展在函数调用的位置,使内联函数看起来象函数,却不需要承受函数调用的开销,对于一些函数体比较简单的内联函数来说,可以大大提高内联函数的调用效率。但内联函数并非没有代价,如果内联函数体比较大,内联函数的扩展将大大增加目标文件和可运行文件的大小;另外,inline关键字对编译器只是一种提示,并非一个强制指令,也就是说,编译器可能会忽略某些inline关键字,如果被忽略,内联函数将被当作普通的函数调用,编译器一般会忽略一些复杂的内联函数,如函数体中有复杂语句,包括循环语句、递归调用等。所以,内联函数的函数体定义要简单,否则在效率上会得不偿失。
静态函数的调用,如下面的几种方式:
X obj; X* ptr = &obj;
obj.StaticFunc();
ptr->StaticFunc();
X::StaticFunc();
将被编译器转化为一般的C函数调用形式,如同这样:
mangled_name_of_X_StaticFunc();
//obj.StaticFunc();
mangled_name_of_X_StaticFunc();
// ptr- >StaticFunc();
mangled_name_of_X_StaticFunc();
// X::StaticFunc();
mangled_name_of_X_StaticFunc()是指编译器将X::StaticFunc()函数经过变形(mangled)后的内部名称(C++(www.cppentry.com)编译器保证每个函数将被mangled为独一无二的名称,不同的编译器有不同的算法,C++(www.cppentry.com)标准并没有规定统一的算法,所以mangled之后的名称也可能不同)。可以看出,静态函数的调用同普通的C函数调用有完全相同的效率,并没有额外的开销。
普通成员函数的调用,如下列方式:
X obj; X* ptr = &obj;
obj.NormalFunc();
ptr->NormalFunc();
将被被编译器转化为如下的C函数调用形式,如同这样。
mangled_name_of_X_NormalFunc(&obj);
//obj.NormalFunc();
mangled_name_of_X_NormalFunc(ptr);
// ptr- >NormalFunc();
可以看出普通成员函数的调用同普通的C调用没有大的区别,效率与静态函数也相同。编译器将重新改写函数的定义,增加一个const X* this参数将调用对象的地址传送进函数。
虚函数的调用稍微复杂一些,为了支持多态性(其基础是函数重载),实现运行时刻绑定,编译器需要在每个对象上增加一个字段也就是vptr以指向类的虚函数表vtbl.