More Effective C++ 条款34 如何在一个程序中结合C++和C
1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题.
2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意:
1). name mangling(名称重整)
Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数具有独一无二的名称,由于C不支持函数重载,因此也就不需要name mangling.
如果C++要调用C函数库的某个函数,如果不抑制name mangling,经由name mangling后的函数名称可能与C函数库的原函数名称不匹配,因此就会导致链接失败.解决方法就是使用extern C阻止name mangling,像这样:
extern "C" void simulate(int iterations);
这样C++编译器在编译C++文件时就会避免对simulate进行name mangling,转而采用和C相同的命名方式.extern "C"只对接下来的一个函数有效,如果要同时为多个函数使用extern "C",可以使用中括号:
extern "C" { void drawLine(int x1, int y1, int x2, int y2); void twiddleBits(unsigned char bits); void simulate(int iterations); ... }
有时代码既有可能被C++使用也有可能被C使用:当用于C++时,可能需要使用extern "C",而用于C时不需要,可以利用C++预定义的的__cplusplus宏使得文件可以"通用于数种语言",像这样:
#ifdef __cplusplus extern "C" { #endif void drawLine(int x1, int y1, int x2, int y2); void twiddleBits(unsigned char bits); void simulate(int iterations); ... #ifdef __cplusplus } #endif
(注:如果要结合使用C,assembler,Fortran,LISP,Forth等的函数库,也需要抑制name mangling而使用extern "C".并没有像extern "Fortran"这样的用法,因为extern "C"并不说函数以C写成,而是指明要抑制name mangling. )
具体用法参照:http://www.cnblogs.com/luxiaoxun/p/3405374.html
2). Statics的初始化
程序的入口实际上并不是main,而是编译器提供的特殊函数(如 mainCRTStartup(void)等,见C/C++:对象初始化相关),在mainCRTStartup主要任务之一就是全局对象(包括static class对象,全局对象,namespace作用域内的对象以及文件作用域内的对象)的初始化工作,由于C++支持动态初始化(如全局变量int b=a;)而C仅支持静态初始化(见C/C++:对象初始化相关),因此全局对象的动态初始化涉及到动态初始化就应该在C++中撰写main,而将C main重命名为realMain,然后让C++ main调用realMain,像这样:
extern "C" // implement this int realMain(int argc, char *argv[]); // C程序库的函数,用于完成主函数功能 int main(int argc, char *argv[]) // C++代码 { return realMain(argc, argv); }
3). 动态内存分配
C动态内存分配和释放使用malloc(及其变种)和free函数,只涉及内存的分配和释放;而C++使用new和delete关键字,并自动调用对象的构造函数.malloc(及其变种)/free搭配使用,new/delete搭配使用,不能混淆.
(注:个人认为只要彻底了解new和delete的内部机制,混用问题也不大,见Effective C++ 条款16)
4). 数据结构的兼容性
C和C++对于struct和内置类型变量是兼容的,因为C/C++对于struct的内存布局相同,但如果C++为struct加上非虚成员函数,由于struct内存布局不改变,其对象仍兼容于C,但如果加上虚函数,由于vtbl和vptr的存在,struct内存布局便发生改变,也就不再兼容于C.
也就是说,在C和C++之间对数据结构做双向交流是安全的——前提是结构的定义式在C和C++中都可编译;如果为C++ struct加上非虚函数,虽然不再兼容于C,但可能不影响兼容性;其他改变如虚函数和继承等则会产生影响.