分享
 
 
 

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

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

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

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有