首页 | 资讯动态 | linux基础 | 系统管理 | 网络管理 | 编程开发 | linux数据库 | 服务器技术 | linux相关 | linux认证 | 嵌入式 | 下载中心 | 专题 | linux招聘 | 镜像站
OKLinux中文技术站
·设为首页
·加入收藏
·联系我们
系统管理: 中文环境 系统管理 桌面应用 内核技术 | Linux基础: 基础入门 安装配置 常用命令 经验技巧 软件应用 | Linux数据库: Mysql Postgre Oracle DB2 Sybase other
网络管理: 网络安全 网络应用 Linux服务器 环境配置 黑客安全 | 编程开发: PHP CC++ Python Perl Shell 嵌入式开发 java jsp | PHP技术: PHP基础 PHP技巧 PHP应用 PHP文摘
Linux资讯 Linux招聘 Linux专题 Apache | Linux相关: 硬件相关 Linux解决方案 Linux认证 企业应用 其它Unix | 相关下载: 资料下载 参考手册 开发工具 服务器类 软路由 其它
 技术搜索:
会员中心 注册会员 高级搜索  
  → 当前位置:首页>编程开发>cc++>正文

C++的效率浅析

http://www.oklinux.cn  2003-05-01  来源: 互联网  本站整理   会员收藏  游客收藏  【 】 
   自从七十年代C语言诞生以来,一直以其灵活性、高效率和可移植性为软件开发人员所钟爱,成为系统软件开发的首选工具。而C++作为C语言的继承和发展,不仅保留了C语言的高度灵活、高效率和易于理解等诸多优点,还包含了几乎所有面向对象的特征,成为新一代软件系统构建的利器。

   相对来说,C语言是一种简洁的语言,所涉及的概念和元素比较少,主要是:宏(macro)、指针(pointer)、结构(struct)、函数(function)和数组(array),比较容易掌握和理解。而C++不仅包含了上面所提到的元素,还提供了私有成员(private members)、公有成员(public members)、函数重载(function overloading)、缺省参数(default parameters)、构造函数、析构函数、对象的引用(references)、操作符重载(operator overloading)、友元(friends)、模板(templates)、异常处理(exceptions)等诸多的要素,给程序员提供了更大的设计空间,同时也增加了软件设计的难度。

   C语言之所以能被广泛的应用,其高效率是一个不可忽略的原因,C语言的效率能达到汇编语言的80%以上,对于一种高级语言来说,C语言的高效率就不言而喻了。那么,C++相对于C来说,其效率如何呢?实际上,C++的设计者stroustrup要求C++效率必须至少维持在与C相差5%以内,所以,经过精心设计和实现的C++同样有很高的效率,但并非所有C++程序具有当然的高效率,由于C++的特殊性,一些不好的设计和实现习惯依然会对系统的效率造成较大的影响。同时,也由于有一部分程序员对C++的一些底层实现机制不够了解,就不能从原理上理解如何提高软件系统的效率。

   本文主要讨论两个方面的问题:第一,对比C++的函数调用和C函数调用,解析C++的函数调用机制;第二,例举一些C++程序员不太注意的技术细节,解释如何提高C++的效率。为方便起见,本文的讨论以下面所描述的单一继承为例(多重继承有其特殊性,另作讨论)。

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++的的函数分为四种:内联函数(inline member function)、静态成员函数(static member function)、虚函数(virtual member function)和普通成员函数。

   内联函数类似于C语言中的宏定义函数调用,C++编译器将内联函数的函数体扩展在函数调用的位置,使内联函数看起来象函数,却不需要承受函数调用的开销,对于一些函数体比较简单的内联函数来说,可以大大提高内联函数的调用效率。但内联函数并非没有代价,如果内联函数体比较大,内联函数的扩展将大大增加目标文件和可运行文件的大小;另外,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++编译器保证每个函数将被mangled为独一无二的名称,不同的编译器有不同的算法,C++标准并没有规定统一的算法,所以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,如类X的对象模型如下图所示(本文中对此不多做解释,若想进一步了解,可以参考其它材料)。



   虚函数的多态性只能通过对象指针或对象的引用调用来实现,如下的调用:

X obj; X* ptr = &obj; X& ref = obj; ptr->VirtualFunc(); ref.VirtualFunc();

   将被C++编译器转换为如下的形式。

( *ptr->vptr[2] )(ptr); ( *ptr->vptr[2] )(&ref);

   其中的2表示VirtualFunc在类虚函数表的第2个槽位。可以看出,虚函数的调用相当于一个C的函数指针调用,其效率也并未降低。

   由以上的四个例子可以看出,C++的函数调用效率依然很高。但C++还是有其特殊性,为了保证面向对象语义的正确性,C++编译器会在程序员所编写的程序基础上,做大量的扩展,如果程序员不了解编译器背后所做的这些工作,就可能写出效率不高的程序。对于一些继承层次很深的派生类或在成员变量中包含了很多其它类对象(如XX中的m_strName变量)的类来说,对象的创建和销毁的开销是相当大的,比如XX类的缺省构造函数,即使程序员没有定义任何语句,编译器依然会给其构造函数扩充以下代码来保证对象语义的正确性:

XX::XX() { // 编译器扩充代码所要做的工作 1、 调用父类X的缺省构造函数 2、 设定vptr指向XX类虚函数表 3、 调用String类的缺省构造函数构造m_strName };

   所以为了提高效率,减少不必要的临时对象的产生、拖延暂时不必要的对象定义、用初始化代替赋值、使用构造函数初始化列表代替在构造函数中赋值等方法都能有效提高程序的运行效率。以下举例说明:

   1、 减少临时对象的生成。如以传送对象引用的方式代替传值方式来定义函数的参数,如下例所示,传值方式将导致一个XX临时对象的产生

效率不高的做法     高效率做法 void Function( XX xx ) void Function( const XX& xx ) { { //函数体 //函数体 } }

   2、 拖延暂时不必要的对象定义。在C中要将所有的局部变量定义在函数体头部,考虑到C++中对象创建的开销,这不是一个好习惯。如下例,如果大部分情况下bCache为"真",则拖延xx的定义可以大大提高函数的效率。

效率不高的做法 高效率做法 void Function( bool bCache ) void Function( bool bCache ) { { //函数体 //函数体 XX xx; if( bCache ) if( bCache ) {// do something without xx { return; // do something without xx } return; } //对xx进行操作 XX xx; //对xx进行操作 … return; return; } }

   3、 可能情况下,以初始化代替先定义后赋值。如下例,高效率的做法会比效率不高的做法省去了cache变量的缺省构造函数调用开销。

效率不高的做法 高效率做法 void Function( const XX& xx ) void Function( const XX& xx ) { { XX cache; XX cache = xx; cache = xx ; } }

   4、 在构造函数中使用成员变量的初始化列表代替在构造函数中赋值。如下例,在效率不高的做法中,XX的构造函数会首先调用m_strName的缺省构造函数,再产生一个临时的String object,用空串""初始化临时对象,再以临时对象赋值(assign)给m_strName,然后销毁临时对象。而高效的做法只需要调用一次m_strName的构造函数。

效率不高的做法 高效率做法 XX::XX() XX::XX() : m_strName( "" ) { { m_strName = ""; … … } }

   类似的例子还很多,如何写出高效的C++程序需要实践和积累,但理解C++的底层运行机制是一个不可缺少的步骤,只要平时多学习和思考,编写高效的C++程序是完全可行的。

上一篇:C++的中抽象   下一篇:C++的算符重载


收藏于收藏夹】 【评论】 【推荐】 【打印】 【关闭
相关文档
·C++的算符重载
·C++的中抽象
·C++对象的放置
·C++的数据类型转换关键字dynamic_cast
·C++和Object Pascal对象模型(1)
·C++的类和类的定义
·C++程序设计例解(05)
·C++和Object Pascal对象模型(2)
·C++实例教学(基础知识-01)
·C++程序设计例解(04)
·C++辨析系列谈
·C++实例教学(基础知识-02)
·C++Builder中异形按钮的实现
·C++实例教学(基础知识-03)
·C++Builder中实现控件数组的功能
·C++实例教学(类的应用
发表评论
密码: 匿名评论
评论内容:

(不超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规)
 
  最新文档
·用Eclipse平台进行C/C 开发
·在 Linux 中使用共享对象
·VS:针对Java开发人员的C#编程语言
·使用智能设备扩展在C#中开发自定义控件
·Visual C# 常见问题
·二级C语言实例解答
·一种被忽视的构造和整数溢出重现
·轻轻松松C to C
·与用于 C 的ISO标准保持一致
·用C 的托管扩展针对Windows编程
·运行时和编译时的安全性检查
·轻轻松松C to C (二)
  阅读排行
·c/c++ 学习-read 函数和 write 函数
·程序员眼中的qmail(qmail源代码分析)
·Awk 基础入门:Awk 实例编程
·autoconf 和automake生成Makefile文件
·Linux下的多进程编程
·入门文章:教你学会编写Linux设备驱动
·使用 GDB 调试多进程程序
·C++自动化(模板元)编程基础与应用
·Qt 不规则窗体的实现
·嵌入式程序员应知道的几个基本问题
·Linux操作系统中GCC的应用介绍
·C语言中的指针和内存泄漏
·用GNU profiler提高代码运行速度
·vi 中的正则表达式 (Regular Expressio
·Linux 套接字编程中的 5 个隐患
网摘收藏: