虚拟继承,虚基类

    文章出处:http://blog.csdn.net/skylor/archive/2009/03/26/4025698.aspx

    虚拟继承与虚基类实际上是说了同一件事,只是不同的书表达不同,在这里还是推荐虚拟继承这种说法(因为总有人问虚基类是什么,这里可以解释为虚基类就是虚拟继承,一种继承的方式,有的书偏要把一个动作写成一个名词,不负责任)。虚拟继承是C++继承的一个特殊方法,用来达到特殊的目的。要达到什么目的呢?那就是避免继承机制下的二义性问题二义性:程序产生两种或多种可能,把编译器搞的不知所措

    继承机制下的二义性一般体现在两种情况下,要介绍的虚拟继承主要解决了其中第二种情况的二义性问题,不妨把两种情况都简单说一说:

    1.  第一种情况:由多个基类同名成员产生的二义性

    class A  //定义一个类A

    {

        public:

            A(){ cout<<"A called"<<endl; }

            void print(){ cout<<"A print"<<endl; }

        private:

    };

    class B  //定义一个类B

    {

        public:

            B(){ cout<<"B called"<<endl; }

            void print(){ cout<<"B print"<<endl; }

        private:

    };

    class C: public A, public B  //定义一个类C分别继承自A, B

    {

        public:

            C(){}

        private:

    };

    int main(void)

    {

        C c;

        c.print(); -----------------------------------mark 1

        getchar();

        return 0;

    }

    如上图代码所示,主程序main()在执行到mark1标记时产生了二义性,c对象有两个基类,编译器不知道该调用A的print()还是B的print(),这个二义性的产生的解决办法虚拟继承无关,需要用作用域符号来解决,即将c.print()修改为c A::print(); 那么就调用A的print方法,也就是告诉他要调用的方法在哪个基类里。

    2.  第二种情况:由多个父类的共同基类产生的二义性

    A是B, C的共同基类,D继承于B, C

    class A

    {

        public:

            A(){ cout<<"A called"<<endl; }

            void print(){ cout<<"A print"<<endl; }

        private:       

    };

    class B : public A

    {

        public:

            B(){ cout<<"B called"<<endl; }

        private:

    };

    class C : public A

    {

        public:

            C(){ cout<<"C called"<<endl; }

        private:

    };

    class D : public B, public C

    {

        public:

            D(){ cout<<"D called"<<endl; }

        private:

    };

    int main( void )

    {

        D d;

        d.print(); -----------------------------------mark 2

        getchar();

        return 0;

    };

    如图和代码所示:主程序main在执行到mark2标记时产生了二义性,虽然只有基类A中有print(),但是继承的路线有两条,编译器不知道从B路线向上找还是从C向上找,一样会出错,这里可以用第一种方法用到的作用域说明符号即将d.print()改为d.B::print(),告诉编译器是从B继承下来的,当然,也可改为d.C::print()。除了这种解决方案,还有另外一种解决方案,就是:运用虚拟继承机制。实际上造成上边的二义性的根本原因在这种继承的特殊模式下,A这个父类分别伴随B和C产生了两个拷贝,在调用拷贝中的方法时产生了矛盾,到底是调用哪一个拷贝中的print()呢?于是,所有人都会想,要是只有一个拷贝就好了,就没有矛盾了,虚拟继承就提供了这种机制,按上面的例子,只需修改B和C对A的继承方式,即加一个关键字virtual

    class B : virtual public A

    {

        public:

            B(){ cout<<"B called"<<endl; }

        private:

    };

    class C : virtual public A

    {

        public:

            C(){ cout<<"C called"<<endl; }

        private:

    };

    这就相当于说,在没有A类的拷贝时就构造一个,如果已经有了,就用已经有的那一个,这样一来,拷贝只有一份了,二义性消除了。

    虚拟继承不多说了,最后再补充点关于继承的东西。实际上继承框架性的东西不多,一个访问控制,一个调用数序,把这两个搞清楚,再把上面的弄明白,就差我下面要说的一件事了,

    有个地方叫继承的支配规则

    派生类中设置了基类中同名的成员,就是说基类的成员的名字在派生类中再次使用,则派生类中的名字就把基类的名字隐藏在其后面了所有的调用都是对派生类成员的调用,除非用域说明符::。

    上例子吧:

    例1:

    class A

    {

        public:

            A(){ cout<<"A called"<<endl; }

            void print(){ cout<<"A print"<<endl; }

        private:       

    };

    class B : public A

    {

        public:

            B(){ cout<<"B called"<<endl; }

            void print(){ cout<<"B print"<<endl; }

        private:       

    };

    int main(void)

    {

        B b;

        b.print();

        getchar();

        return 0;

    }

    打印的肯定是"B print",那要想打印"A print"呢,只能改为b.A::print();

    需要说明的是这里并不是对print的重载(根本是同一个方法嘛,与重载没关系),应该起名叫“隐藏”比较合适。

    例2:

    class A

    {

        public:

            A(){ cout<<"A called"<<endl; }

            void print(){ cout<<"A print"<<endl; }

        private:       

    };

    class B : public A

    {

        public:

            B(){ cout<<"B called"<<endl; }

            void print( int a ){ cout<<"B print"<<endl; }

        private:       

    };

    int main(void)

    {

        B b;

        b.print();

        getchar();

        return 0;

    }

    这个例子编译一下,没通过!怎么可能呢,B的print参数不符合,不是基类的print符合要求吗,怎么不调用呢。原来在派生类B中,print被更改了,这里是被重载了,和多人把这个叫做“覆盖”,有点道理,所以A类中的print()怎么也访问不到,除非用作用域符号,怎么改呢,加个参数,加个作用域符号,看你想怎么用了,在这里只是想说明“覆盖”这么件事。

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(c,框架,Class,编译器)