C++如何处理内联虚函数

原创 2006年05月24日 11:16:00

  当一个函数是内联和虚函数时,会发生代码替换或使用虚表调用吗? 为了弄清楚内联和虚函数,让我们将它们分开来考虑。通常,一个内联函数是被展开的。

        class CFoo {
        private:
            int val;
        public:
         int GetVal() { return val; }
         int SetVal(int v) { return val=v; }
        };       
这里,如果使用下列代码:        
 CFoo x;
        x.SetVal(17);
        int y = x.GetVal(); 
那么编译器产生的目标代码将与下面的代码段一样:        
 CFoo x;
        x.val = 17;
        int y = x.val; 
     你当然不能这么做,因为val是个私有变量。内联函数的优点是不用函数调用就能隐藏数据,仅此而已。

     虚函数有多态性,意味着派生的类能实现相同的函数,但功能却不同。假设 GetVal 被声明为虚函数,并且你有第二个 以不同方法实现的类 CFoo2:        
 class CFoo2 : public CFoo {
        public:
        // virtual in base class too!
        virtual int CFoo2::GetVal() { return someOtherVal; }
        };
     如果 pFoo是一个 CFoo 或 CFoo2 指针,那么,无论 pFoo 指向哪个类 CFoo 或 CFoo2,成员函数 pFoo->GetVal 都能调用成功。

     如果一个函数既是虚拟函数,又是内联函数,会是什么情况呢?记住,有两种方式建立内联函数,

第一种是在函数定义中使用关键字 inline,如:        
 inline CFoo::GetVal() { return val; }       
第二种是在类的声明中编写函数体,就象前面的 CFoo2::GetVal 一样。所以如果将虚函数体包含在类的声明中,如:        
 class CFoo {
        public:
        virtual int GetVal() { return val; }
        };       
     编译器便认为这个函数 GetVal 是内联的,同时也是虚拟的。那么,多态性和内联特性如何同时工作呢?

     编译器遵循的第一个规则是无论发生什么事情,多态性必须起作用。如果有一个指向 CFoo 对象的指针,pFoo->GetVal 被保证去调用正确的函数。一般情况下,这就是说函数 GetVal 将被实例化为非内联函数,并有vtable(虚表)入口指向它们。但这并不意味着这个函数不能被扩展!再看看下面的代码:        
 CFoo x;
        x.SetVal(17)
        int y = x.GetVal() 
     编译器知道x是 CFoo,而不是CFoo2,因为这个堆对象是被显式声明的。x肯定不会是CFoo2。所以展开 SetVal/GetVal 内联是安全的。如果要写更多的复杂代码:  
 CFoo x;
 CFoo* pfoo=&x;
 pfoo->SetVal(17);
 int y = pfoo->GetVal();
 ...
 CFoo2 x2;
 pfoo = &x2;
 pfoo->SetVal(17); //etc.
  编译器知道 pfoo 第一次指向x,第二次指向x2,所以展开虚拟函数也是安全的。

     你还可以编写更复杂的代码,其中,pfoo 所指的对象类型总是透明的,但是大多数编译器不会做任何更多的分析。即使在前面的例子中,某些编译器将会安全运行,实例化并通过一个虚表来调用。实际上,编译器总是忽略内联需要并总是使用虚表。唯一绝对的规则是代码必须工作;也就是说,虚函数必须有多态行为。
     通常,无论是显式还是隐式内联,它只是一个提示而已,并非是必须的,就象寄存器一样。编译器完全能拒绝展开一个非虚内联函数,C++编译器常常首先会报错:“内联中断-函数太大”。如果内联函数调用自身,或者你在某处传递其地址,编译器必须产生一个正常(外联?)函数。内联函数在DEBUG BUILDS中不被展开,可设置编译选项来预防。
     要想知道编译器正在做什么,唯一的方法是看它产生的代码。对于微软的编译器来说,你可以用-FA编译选项产生汇编清单。你不必知道汇编程序如何做。我鼓励你完成这个实验;这对于了解机器实际所做的事情机器有益,同时你可学习许多汇编列表中的内容。
     有关内联函数的东西比你第一次接触它时要复杂得多。有许多种情况强迫编译器产生正常函数:递归,获取函数地址,太大的那些函数和虚函数。但是如果编译器决定实例化你的内联函数,就要考虑把函数放在什么地方?它进入哪个模块?
     通常类在头文件中声明,所以如果某个cpp包含foo.h,并且编译器决定实例化CFoo::GetVal,则在cpp文件中将它实例化成一个静态函数。如果十个模块包含foo.h,编译器产生的虚函数拷贝就有十个。实际上,可以用虚表指向不同类型的GetVal拷贝,从而是相同类型的对象只产生拷贝。一些链接器能巧妙地在链接时排除冗余,但一般你是不能指望他来保证的。
     我们得出的结论是:最好不要使用内联虚函数,因为它们几乎不会被展开,即便你的函数只有一行,你最好还是将它与其它的类函数一起放在模块(cpp文件)中。当然,开发者常常将简短的虚函数放在类声明中-不是因为他们希望这个函数被展开为内联,而是因为这样做更方便和可读性更强。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C++如何处理内联虚函数

当一个函数是内联和虚函数时,会发生代码替换或使用虚表调用吗? 为了弄清楚内联和虚函数,让我们将它们分开来考虑。通常,一个内联函数是被展开的。        01.class CFoo ...

C++如何处理内联虚函数

C++如何处理内联虚函数 分类: C/C++2007-09-07 09:02 1946人阅读 评论(1) 收藏 举报     当一个函数是内联和虚函数时,会发生代码替换或使用虚表调用吗? ...

C++如何处理内联虚函数

当一个函数是内联和虚函数时,会发生代码替换或使用虚表调用吗? 为了弄清楚内联和虚函数,让我们将它们分开来考虑。通常,一个内联函数是被展开的。 class CFoo{ private: int ...

【C++】内联inline、继承、重载与虚函数的解释

1.内联inline inline的作用是提高代码的执行效率; 通过在函数实现体前面增加 inline 关键字,可以将函数定义为内联函数; 在函数定义内部,若直接完成了成员函数的实现,那么函数自...

虚函数什么情况下会内联

转载自:http://blog.csdn.net/zy1691/article/details/3606128 最近有个朋友研究了一下虚函数内联的问题,我也对这个问题产生了兴趣,发现...

c++ 内联成员函数

内联成员函数 类中的成员函数分为一般成员函数和内联成员函数。 把成员函数的代码放入类声明中,这种方式定义的类成员函数就叫内联成员函数(隐式定义)。程序使用内联成员函数的方式与一般的成员函数的调用方...

c++ inline(内联)函数

(一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联。 inline int min(int first, i...

C++进阶 - 函数特性总结(重载与内联)

个人之言,请持怀疑态度参考。 C++有重载、内联、const和virtual 四种新的机制。本篇博客主要探究重载与内联。const用发可以参考我的另外一篇文章 点击这里重载和内联机制,既可以用于全局函...

C++知识碎片整理(7)——inline(内联)函数

看caffe源代码看到了一些函数前有这样一个关键词,了解一下它的功能。 2016/9/1 简单理解inline 说明这个函数是内联的,在编译过程中内联函数会直接被源代码替换,提高执行效率。如果类中的...

11.C++ 内联成员函数、对象、

内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)