设为首页 加入收藏

TOP

C++语言笔记系列之十七――虚基类
2015-07-24 05:42:52 来源: 作者: 【 】 浏览:5
Tags:语言 笔记 系列 十七
1.虚基类
考虑这样一种情况:当某个类的部分或者全部直接基类是另一个共同基类派生而来,这些直接基类从上一级基类继承而来的成员就一定拥有相同的名称,这样就会产生二义性问题。
解决办法:当派生类和直接基类产生了二义性问题-->加类的作用域。
当派生类和间接基类产生了二义性问题-->虚基类。
2.虚基类的说明:
class 派生类名:virtual 访问权限 基类名
{派生类定义};
注意:在定义派生类时将需要继承的基类进行虚化声明,虚基类的说明在派生类的定义中完成。
作用:将基类说明为虚基类之后,无论虚基类产生多少个派生类都不产生基类副本――多个派生类共用一个基类。
注意:若一个基类派生出多个派生类,必须在每个派生类的定义中将基类声明为虚基类,任何一个派生类未进行声明,那么 系统就会为该派生类产生基类副本。
example 1

class One
{int b;};
class Ba seone:virtual public One
{int b1;};
class Basetwo:virtual public One
{int b2;};
class Basethree:public Baseone, public Basetwo
{};
3.虚基类的初始化
(1)派生类构造函数的调用顺序:基类构造-->子对象构造-->派生类构造。
(2)虚基类的构造在非虚基类构造之前完成,若同一层有两个虚基类,按说明顺序调用构造。(3)虚基类只构造一次。
(4)若虚基类是非虚基类派生类,则先调用非虚基类构造。
(5)析构顺序严格与构造相反。

example 2

#include
class Base1
{
public:
Base1()
{
cout<<"Base1 construction."< }
};
class Base2
{
public:
Base2()
{
cout<<"Base2 construction."< }
};
class Level1:public Base2, virtual public Base1
{
public:
Level1() {cout<<"Level1 construction."< };
class Level2:public Base2, virtual public Base1
{
public:
Level2() {cout<<"Levle2 construction."< };
class Toplevel:public Level1, virtual public Level2
{
public:
Toplevel() {cout<<"Toplevel construction."< };

int main()
{
Toplevel obj;
}
程序输出:
Base1 construction.
Base2 construction.
Levle2 construction.
Base2 construction.
Level1 construction.
Toplevel construction.
分析:obj是Toplevel的对象,Toplevel有一个虚基类和一个非虚基类,按照规则,先构造虚基类,即Level2。而Level2又有一个虚基类和非虚基类,同样先构造虚基类,即Base1,Base1构造之后会构造Level2的非虚基类Base2,最后才构造Level2。至此,Toplevel的虚基类构造完成,下面该构造TopLevel的非虚基类Level1,首先构造器虚基类Base1,因为Base1已经构造过,所以不再构造,下面是Base2,最后是Level1。最后一步就是
构造Toplevel了。

example 3

#include
class A
{
public:
A(char i) {cout<<"A construction "< ~A() {cout<<"A destruction."< };
class B:virtual public A
{
public:
B(char i, char j):A(i)//由于虚基类A属于有参构造,所以必须在派生类B中调用A的构造
{
cout<<"B construction "< }
~B() {cout<<"B destruction."< private:
char b;
};
class C:virtual public A
{
public:
C(char i, char j):A(i)//尽管派生类C并没有调用虚基类A的构造函数,因为在派生类B中已经
//调用过,但C的构造函数中必须要声明对虚基类A的有参调用。
{
cout<<"C construction "< }
};
class D:public B, public C
{
public:
D(char i, char j, char k, char l, char m, char n):
C(k,l), B(i,j), A(n), aa(m)
{
cout<<"D construction."< }
private:
A aa;
};

int main()
{
D obj('a', 'b', 'c', 'd', 'e', 'f');
}
程序输出:
A construction f
B construction b
C construction d
A construction e
D construction.
A destruction.
B destruction.
A destruction.
注意:在D的构造函数中有A(i),这个位置加上A(i)是必须的。A的构造用的是f。
分析:在D构造中加A(i)是必须的,因为A的构造会在B和C的构造之前,也就是说传给B和C的参数是不能用来构造A的,所以A的有参构造必须来自于D,并且取的是D传递给A的参数值。
4.调用顺序
(1)所有虚基类构造函数按照它们被继承的顺序构造。
(2)所有非虚基类的构造函数按照它们被继承的顺序构造。
(3)所有子对象按照它们的声明顺序构造。
(4)派生类自己的构造:
A.在一个初始化列表中同时出现虚基类和非虚基类成员函数的调用,虚基类优先于非虚基类。
B.同一层的虚基类按照说明顺序调用构造,非虚基类按照继承顺序调用构造。
C.所有的虚基类在继承中只构造一次,虚基类的每个子对象都要调用一次构造。
5.虚基类的赋值兼容规则
(1)派生类地址可以赋值给虚基类指针。
(2)虚基类引用可以引用派生类对象。(都是大的可以赋值给小的)
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇K倍动态减法游戏 HDU 2486&&a.. 下一篇Light OJ 1304 The Best Contest ..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: