======================================================
大家请把我的文章当参考,详细内容 还请参照 权威书
籍如果文中有错误和遗漏, 请指出,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.
今天说了好多东西,下次见!祝大家过的愉快。