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

http://blog.csdn.net/hedylin/article/details/1775556

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

        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文件)
中。当然,开发者常常将简短的虚函数放在类声明中-不是因为他们希望这个函数被展开为内联,而是因为这样做更方便和可读性更强

时间: 2024-10-06 16:09:08

C++如何处理内联虚函数的相关文章

sql server 创建内联表值函数

表值函数就是返回table 的函数使用它可以方便的进行查询的处理 创建的代码如下: create FUNCTION returunclassfirstlist(  -- Add the parameters for the function here )RETURNS TABLE ASRETURN ( -- Add the SELECT statement with parameter references here select * from classfirst;) 我们在使用创建的函数的时

内联表值函数FUNCTION

创建一个内联表值函数: 1 USE TSQLFundamentals2008; 2 IF OBJECT_ID('dbo.fn_GetCustOrders') IS NOT NULL 3 DROP FUNCTION dbo.fn_GetCustOrders; 4 GO 5 CREATE FUNCTION dbo.fn_GetCustOrders 6 (@cid AS INT) RETURNS TABLE 7 AS 8 RETURN 9 SELECT orderid, custid, empid,

C++中的内联成员函数与非内联成员函数

在C++中内联成员函数与非内联成员函数的可以分为两种情况: 1.如果成员函数的声明和定义是在一起的,那么无论有没有写inline这个成员函数都是内联的,如下: using namespace std; class test{ public: void fuc() { cout << "ok!" << endl; } }; int main(void) { test t, t1; t.fuc(); t1.fuc(); return 0; } 或者: using n

内联成员函数应放在哪

今天复习C++ Primer的时候,看到了关于C++类的内联成员函数的放置,应该放在头文件中.那么这到底是为什么 呢?仅仅是一种代码规范问题还是必须这样做呢? 下面我就来讲讲我自己的理解吧.要彻底理解这个问题,首先就要了解下函数的声明和定义了.我们知道,函数可以 在多处声明,但只能在一个地方定义,不然就会出现重定义.大部分函数默认是外部链接,而inline函数默认为内部链 接.也就是说inline函数只能在本文件中使用,对其他文件是不可见的.一般我们使用某个类的时候,都是在文件中加 上该类的头文

内联表值函数

内联表值函数是一种可重用的表表达式,能够支持输入参数.除了支持输入参数以外,内联表值函数在其他方面都与视图相似.(可以将内联表值函数看作是一种参数化的视图,尽管没有这种正式的说法). 例: CREATE FUNCTION fn_GetCustOrders (@cid as int) RETURNS TABLE AS RETURN SELECT orderid,custid,empid,orderdate,requiredate FROM dbo.Orders WHERE [email prote

为什么 构造函数、内联函数、静态函数和友元函数不能是虚函数

构造函数为什么不能是虚函数 C++ 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的.问题出来了,如果构造函数是虚的,就需要通过vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数.简单来说就是:虚函数的执行依赖于虚函数表.而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表.而在构造对象期间,虚函数表还没有被初始化,将无法进行

内联函数详解

什么是内联性和外联函数 类的成员函数可以分为内联函数和外联函数.内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内.而说明在类体内,定义在类体外的成员函数叫外联函数.外联函数的函数体在类的实现部分. 内联函数在调用时不是像一般的函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函数中,执行其后语句,而是在调用函数处用内联函数体的代码来替换,这样将会节省调用开销,提高运行速度. 内联函数与前面讲过的带参数的宏定义进行一下比较,它们的代码效率是一样的,但是内联函数要优于宏定义

C++ 内联函数inline

http://blog.csdn.net/u011327981/article/details/50601800 1.  内联函数 在C++中我们通常定义以下函数来求两个整数的最大值: 复制代码 代码如下: int max(int a, int b){ return a > b ? a : b;} 为这么一个小的操作定义一个函数的好处有: ① 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多 ② 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多

C++内联函数的使用

1.为什么要用内联函数? 在C++中我们通常定义以下函数来求两个整数的最大值: int max(int a, int b) { return a > b ? a : b; } 为这么一个小的操作定义一个函数的好处有: ① 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多 ② 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多 ③ 使用函数可以确保统一的行为,每个测试都保证以相同的方式实现 ④ 函数可以重用,不必为其他应用程序重写代码 虽然有这么多