C++箴言:理解inline化的介入和排除
我们再讨论。 在做这件事之前,我们先来完成对这个结论的考察:inline 是一个编译器可能忽略的请求。大多数编译器拒绝它们认为太复杂的 inline 函数(例如,那些包含循环或者递归的),而且,除了最细碎的以外的全部虚拟函数的调用都不会被 inline 化。不应该对这后一个结论感到惊讶。虚拟意味着“等待,直到运行时才能断定哪一个函数被调用”,而 inline 意味着“执行之前,用被调用函数取代调用的地方”。如果编译器不知道哪一个函数将被调用,你很难责备它们拒绝 inline 化这个函数本体。 所有这些加在一起,得出:一个被指定的 inline 函数是否能真的被 inline 化,取决于你所使用的构建环境——主要是编译器。幸运的是,大多数编译器都有一个诊断层次,在它们不能 inline 化一个你提出的函数时,会导致一个警告。 有时候,即使当编译器完全心甘情愿地 inline 化一个函数,他们还是会为这个 inline 函数生成函数本体。例如,如果你的程序要持有一个 inline 函数的地址,编译器必须为它生成一个 outlined 函数本体。他们怎么能生成一个指向根本不存在的函数的指针呢?再加上,编译器一般不会对通过函数指针的调用进行 inline 化,这就意味着,对一个 inline 函数的调用可能被也可能不被 inline 化,依赖于这个调用是如何做成的: inline void f() {...} // assume compilers are willing to inline calls to f
甚至在你从来没有使用函数指针的时候,未 inline 化的 inline 函数的幽灵也会时不时地拜访你,因为程序员并不必然是函数指针的唯一需求者。有时候编译器会生成构造函数和析构函数的 out-of-line 拷贝,以便它们能得到指向这些函数的指针,在对数组中的对象进行构造和析构时使用。 事实上,构造函数和析构函数对于 inline 化来说经常是一个比你在不经意的检查中所能显示出来的更加糟糕的候选者。例如,考虑下面这个类 Derived 的构造函数: class Base {
这个构造函数看上去像一个 inline 化的极好的候选者,因为它不包含代码。但是视觉会被欺骗。 C++ 为对象被创建和被销毁时所发生的事情做出了各种保证。例如,当你使用 new 时,你的动态的被创建对象会被它们的构造函数自动初始化,而当你使用 delete。则相应的析构函数会被调用。当你创建一个对象时,这个对象的每一个基类和每一个数据成员都会自动构造,而当一个对象被销毁时,则发生关于析构的反向过程。如果在一个对象构造期间有一个异常被抛出,这个对象已经完成构造的任何部分都被自动销毁。所有这些情节,C++ 只说什么必须发生,但没有说如何发生。那是编译器的实现者的事,但显然这些事情不会自己发生。在你的程序中必须有一些代码使这些事发生,而这些代码——由编译器写出的代码和在编译期间插入你的程序的代码——必须位于某处。有时它们最终就位于构造函数和析构函数中,所以我们可以设想实现为上面那个声称为空的 Derived 的构造函数生成的代码就相当于下面这样: Derived::Derived() // conceptual implementation of |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |