游戏开发论坛

 找回密码
 立即注册
搜索
查看: 12853|回复: 10

C++基本功和 Design Pattern系列(9) virtual

[复制链接]

27

主题

179

帖子

259

积分

中级会员

Rank: 3Rank: 3

积分
259
发表于 2006-12-17 03:03:00 | 显示全部楼层 |阅读模式
======================================================
大家请把我的文章当参考,详细内容 还请参照 权威书
籍如果文中有错误和遗漏, 请指出,Aear会尽力更正,
谢谢!
Aear Blog: http://blog.sina.com.cn/u/1261532101
======================================================

关于virtual 一般有3种用法: virtual function, pure virtual function, 和 virtual inheritance. 今天就分别来讲讲这几种virtual.

================ virtual function ================
virtual函数是在类里边的一种特殊的函数,至于具体的含义,相信大家都知道,我就不多说了。而virtual最基本的思想,就是OO语言中多态的体现。把函数从编译时的棒定,转移到运行时的动态绑定。

virtual的好处很多,比如能使程序结构更加清晰,代码的重复利用率更高, 但是也是有代价的。让我们来看下代码:

class Test {
    ...
    virtual void VirtualFunc(void) { cout<<"Test1"<<endl; };
};

class Test2 : public Test {
    ...
    virtual void VirtualFunc(void) { cout<<"Test2"<<endl; };
};

Test * pTest = new Test2();
pTest->VirtualFunc();

上面代码的输出结果是"Test2"。也就是说,虽然你是通过Test * 进行的调用,但真正执行的代码是你创建的Test2的VirtualFunc. 这样的效果是通过在编译的时候创建virtual pointer: _vptr 和 virtual table: _vtbl 来实现的(大部分编译器的实现是通过_vptr & _vtbl)。对于每个有virtual function的class,compiler创建一个_vtbl,用来记录所有的virtual function的地址。同时在所有这个class的instance(实例)里,都有一个_vptr,指向_vtbl。因此, pTest->VirtualFunc() 这段代码等同于:

    (*pTest->_vptr[X])()

其中X是VirtualFunc在_vtbl中对应的位置。(其实对于不同的compiler,都有不同的实现,不同的类层次结构,_vptr和_vtbl的数目也不一定相同。)

从上面的代码考虑,virtual 和 non-virtual的class对比,在性能上有3个方面受到影响:
    1. _vptr是在constructor中由compiler生成的代码隐性的初始化,占用了一定的时间。
    2. virtual function是通过指针间接调用的,比直接调用需要更多的时间,而且在有的情况下会导致CPU的指令缓存失效,从而浪费更多的时间。
    3. 由于virtual function是在运行时进行动态绑定的,所以无法进行inline.

对于第一条,如果是使用 virtual function,那是无可避免的,对于第2条,有通过显式的调用virtual function来提高速度,代码如下:

class Test2 : public Test {
    ...
    virtual void VirtualFunc(void) { cout<<"Test2"<<endl; };
    void VirtualFuncFast(void) { Test2::VirtualFunc(); };
};

由于Test2::VirtualFunc指定了调用函数的版本,所以是在编译时候就绑定了,免去了指针的间接调用过程,而且 VirtualFuncFast本身也是可以inline的。当然了,这样做也是有坏处的,就是VirtualFuncFast本身是Test2的函数,而不是Test的,所以不能通过Test的指针掉用。因此只有在确定是Test2的情况下,通过static_cast才能掉用 VirtualFuncFast.

同理,在一个virtual function里调用另外一个virtual function的时候,使用显试的调用也能提高一定的速度,比如:

class Test2 : public Test {
    ...
    virtual void VirtualFunc(void) { cout<<"Test2"<<endl; };
    virtual void VirtualFunc2(void) { Test2::VirtualFunc(); };
};


对于第3种情况,我们可以通过用Template代替Virtual Function来获得性能上的提高,举个简单的例子:

========== virtual 实现 ==========
class Base{
public:
    virtual void VirtualFunc(void);
};

class Derive1 : public Base{
public:
    virtual void VirtualFunc(void);
};

class Derive2 : public Base{
public:
    virtual void VirtualFunc(void);
};

class SomeClass {
public:
    void test(Base * pBase) { pBase->VirtualFunc(); };
};

// 用法
Test * pTest1 = new Derive1();
Test * pTest2 = new Derive2();
SomeClass Temp1.test(pTest1);
SomeClass Temp2.test(pTest2);

========== 对应的 Template 实现 ==========

class D1{
public:
    // inline here
    void VirtualFunc(void) {};
};

class D2{
public:
    // inline here
    void VirtualFunc(void) {};
};

template <class DCLASS>
class SomeClass {
public:
    // inline here
    void test( void ) { Temp.TestVirtualFunc(); };
private:
    DCLASS Temp;
};

// 用法
SomeClass <D1> Temp.test();
SomeClass <D2> Temp.test();


========== 如何选择 ==========

对于到底是使用 Virtual ,还是使用Template或者Virtual的优化形式, 在大多数情况下不难做出决定。具体选择的过程,只需要问问自己到底是程序的速度重要,还是其他重要,也就是,要在:速度,程序结构,灵活性,易用易维护性,代码的复杂度,代码大小这些中间做出选择。

如果速度排第一位,那么就使用template或者优化,如果其他排在速度的前面,应该尽量的使用virtual。


================ pure virtual function ================

pure virtual function主要是用在abstract class里。有pure virtual function的class,就是abstract class,是不能被实例化的。因此,pure virtual function的代码只有在指定的情况下才能被调用。 例如:

class Test {
    virtual void VirtualFunc(void) = 0 { cout<<"Test"<<endl; };
};


class Test2 : public Test {
    virtual void VirtualFunc(void) { Test::VirtualFunc(); };
};

================ virtual inheritance ================

其实Virtual Inheritance只有可能在Multiple Inheritance中使用。对于Muliple Inheritance, Aear是坚决反对反对再反对的。Aear个人认为,single inheritance是足够用的,Java就是最好的例子。 MI实在是太让人头疼了,所以Aear不会在这个系列中讲MI,这里只说说 Virtual Inheritance和普通的Inheritance的区别。

class Base {
    virtual ~Base();
};

class Derived1 : public Base {
};

class Derived2 : virtual public Base {
};

这里:

sizeof(Derived1) == 4
sizeof(Derived2) == 8

sizeof(Derived1) == 4 是因为 Derived1继承 Base以后,使用Derived1的 _vptr

sizeof(Derived1) == 8 是因为由于在所有的vritual inheritance里,Base作为基类在内存中只能出现一次。所以必须把Base和Derived2的附加部分单独对待。因此有2个_vptr,一个是Base的,一个是Derived2的。

virtual inheritance的直接结果就是大大增加了指令缓存失效的可能性,同时降低了类内部数据的访问速度。因为Base 和 Derived2的内部数据不再是放在连续的内存中了。如果virtual inheritance的层次越多,对运行速度的影响就越大。

所以Aear在这里极度不推荐MI和Virtual Inheritance.

================ virtual 的一些用法 ================
下面是virtual的一些用法。

========== virtual destructor ==========

这个比较简单,就是所有的Base Class,都应该是 public virtual destructor 或者是protected non-virtual destructor,例如:

class Base {
public:
    virtual ~Base();
};

或者

class Base {
protected:
    ~Base();
};

========== virtual constructor ==========

其实本没有virtual constructor,不过C++中有特殊的实现,实现类似virtual constructor 和 virtual copy constructor的。代码如下:

class Base {
    virtual Base * construct (void) { return new Base(); };
    virtual Base * clone(void) { return new Base(*this); };
};

class Derived : public Base {
    virtual Base * construct (void) { return new Derived(); };
    virtual Base * clone(void) { return new Derived(*this); };
};

========== pure virtual destructor ==========

如果我们想设置base class 为abstract class,而又只有一个destructor作为唯一的成员函数,可以把它设成为pure virtual

class Base {
public:
    virtual ~Base() = 0;
};

========== protected & private virtual ==========

对于virtual function是不是应该放在public里边,学术结论和现实中的代码往往不能统一。虽然很多人认为virtual function 不应该放在public里边,但是这样的设计经常会造成编码中的不方便,

实际上对与Java来说,protected 或 private 所有 virtual function,几乎是不太可能的。因此,Aear个人观点,是不是要protected & private virtual function,要根据情况而定。下面是个private virtual的例子:

class Base {
public:
    // this is interface
    void print(void) { output() };
private:
    virtual void output (void) { cout<<"Base"<<endl; };  
};

实际上这样的处理,要比public virtual速度上慢点。

========== 一些要点 ==========

1. 不建议使用MI (Mission Impossible?), 在大多数情况下,它引入的问题,要比它解决的问题多的多的多。(纯个人观点)
2. 类层次结构越多,类的效率越低。
3. Virtual Inheritance的类成员访问效率要低于 Non-virtual Inheritance.

今天说了好多东西,下次见!祝大家过的愉快。

7

主题

438

帖子

438

积分

中级会员

Rank: 3Rank: 3

积分
438
发表于 2006-12-17 11:10:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

1. Virtual Function不应该在Constructor的Destructor中掉用,因为那时候无法确定_vptr是否合法
---
虚函数可以在构造函数中调用,并不会发生虚表指针无效的问题。C++标准对此有特殊处理。调用的是当前构造函数对应的那个类的overriden版本。

36

主题

1047

帖子

1147

积分

金牌会员

Rank: 6Rank: 6

积分
1147
发表于 2006-12-18 20:57:00 | 显示全部楼层

Re: Re:C++基本功和 Design Pattern系列(9) virtual

justlikethewind: Re:C++基本功和 Design Pattern系列(9) virtual

1. Virtual Function不应该在Constructor的Destructor中掉用,因为那时候无法确定_vptr是否合法
---
虚函...

这里应该是纯虚函数不应该在 ctor\dtor 中调用,否则会发生 c++运行时错误。

8

主题

716

帖子

716

积分

高级会员

Rank: 4

积分
716
发表于 2006-12-19 13:12:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

利用中午休息时间,终于一次性看完

下面的VirtualFunc2竟然有virtual应该是Aear的笔误:
class Test2 : public Test {
    ...
    virtual void VirtualFunc(void) { cout<<"Test2"<<endl; };
    virtual void VirtualFunc2(void) { Test2::VirtualFunc(); };
};
不过即使是non-virtual,这种做法也是一个悖论
的确是第一次看到这样的实做法

从弗诺伊德的角度,我猜想aear可能是asm/c出身,或是幼年在效率上有过心里阴影(me 2)
仅以此为例,有些东西是的确是需要保证效率的,但是另一部分,如果盲目追求所谓的效率,却是一种返祖现象,这样一来,不是鼻子长在脑袋上就是6根手指之类。但是怎么看怎么看这篇文章都是很好的啊,为什么总是在让人开始愉悦的同时非要有那么一点“一声叹息”的感觉呢

PS,MI本身没有错,错误是在于错误得理解和使用它的人身上。

16

主题

114

帖子

114

积分

注册会员

Rank: 2

积分
114
发表于 2006-12-19 17:36:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

"对于每个有virtual function的class,compiler创建一个_vtbl,用来记录所有的virtual function的地址。同时在所有这个class的instance(实例)里,都有一个_vptr,指向_vtbl。"

那么对于你给出的例子Test1 和 Test2,就应该有两张不同的_vtbl咯.

但是对单继承的情况下,基类和派生类共享一个_vtbl就好了,这样不是更省空间吗,为什么编译
不去做?

1

主题

7

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2006-12-19 19:26:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

学习新知识。。

8

主题

716

帖子

716

积分

高级会员

Rank: 4

积分
716
发表于 2006-12-20 10:04:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

每次看aear的文章,开始往往会让人有一种感觉是aear在表演心意六合把或八极拳
大气而恢弘
但是打着打着,却总会不经意得使出一招辟邪剑法。。。。。。

学习知识就是一个辩证的过程
将自己的观念公之于众,这即是一个可以加速积累的过程,同时也要求能够冷静面对各方面的指责意见
那么在发表之前,就必须做好准备,尽量得完备
如果只是个人观念参与讨论,各类的奇技淫巧是非常得欢迎
但是作为知识教材或是技术示范,需要注意的地方就多了

27

主题

179

帖子

259

积分

中级会员

Rank: 3Rank: 3

积分
259
 楼主| 发表于 2006-12-21 05:52:00 | 显示全部楼层

Re: Re:C++基本功和 Design Pattern系列(9) virtual

从弗诺伊德的角度,我猜想aear可能是asm/c出身,或是幼年在效率上有过心里阴影(me 2)


Bingo!!! This first program I wrote was in BASIC on apple II, and that was
so slow. Evening drawing a circle took at least half second. So the next
programming language i learned is 6502 assembling language, only for
efficiency. And then, C C++ .......

Actually, in these series, I want present both side of the programming,
the good side, and evil side. There are some un-traditional programming
technologies that do not strictly stick to the Software Engineering and
OO concepts. But I still want to present them ,and it's up to the
programmer to make decision.

Also, for programing, you won't learn anything without writing codes. So
the best way to learn C++ is writing the code in the way you want, make
a huge number of mistakes, and learn from these mistakes. That's way
people can make progress.

And sorry for those who hate english, I don't have chinese inputs on this
computer, so no chinese version for my comments.

BTW, 辟邪剑法 needs to cut JJ to learn ......
[em4] [em10]

7

主题

438

帖子

438

积分

中级会员

Rank: 3Rank: 3

积分
438
发表于 2006-12-21 10:39:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

各位老大开始玩电脑的时间都很早呀。
我刚开始学计算机应用的时候,已经是486的时代了。教育学院里面一万一台的486上面,basic画圆圈还是很快的。

8

主题

553

帖子

560

积分

高级会员

Rank: 4

积分
560
发表于 2006-12-23 17:25:00 | 显示全部楼层

Re:C++基本功和 Design Pattern系列(9) virtual

1. Virtual Function不应该在Constructor的Destructor中掉用,因为那时候无法确定_vptr是否合法
2. 不建议使用MI (Mission Impossible?), 在大多数情况下,它引入的问题,要比它解决的问题多的多的多。
3. 类层次结构越多,类的效率越低。
4. Virtual Inheritance的类成员访问效率要低于 Non-virtual Inheritance.

====================================================
1,2,3我都不认同。
1是完全错误的结论,_vptr在那时是合法的,再翻翻inside c++ object model,实际情况下,很多时候也需要再析构中调用虚函数;
2在现在看来也是不合理的,当然设计模式的东西,很难有统一的意见。但有很多成功开源的项目都使用MI,很多大型的软件在10年前就开始MI了,这些事实多少也说明了什么。它是会带来问题,但楼主的文章里没有列举;
3在没有virtual/multiple inheritance的情况下,我认为一个类如其对应的C struct一样高效。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-26 03:13

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表