C++一些注意点之继承和派生 (四)

2014-11-24 00:59:33 · 作者: · 浏览: 7
0):A(a){y = b;}
void PB(){cout<<"x="< };

class C:public virtual A{
int z;
public:
C(int a=0,int b=0):A(a){z = b;}
void PC(){cout<<"x="< };

class D:public B,public C{
int m;
public:
D(int a,int b,int d,int e,int f):B(a,b),C(d,e)
{
m = f;
}
void PD()
{
PB();
PC();
cout<<"m="< }
};

int main()
{
D d(100,200,300,400,500);//D
d.PD();
d.x=400; //E
d.PD();
return 0;
}

#include

using namespace std;

class A{
public:
int x;
A(int a=0){x = a;}
};

class B:virtual public A{
int y;
public:
B(int a=0,int b=0):A(a){y = b;}
void PB(){cout<<"x="< };

class C:public virtual A{
int z;
public:
C(int a=0,int b=0):A(a){z = b;}
void PC(){cout<<"x="< };

class D:public B,public C{
int m;
public:
D(int a,int b,int d,int e,int f):B(a,b),C(d,e)
{
m = f;
}
void PD()
{
PB();
PC();
cout<<"m="< }
};

int main()
{
D d(100,200,300,400,500);//D
d.PD();
d.x=400; //E
d.PD();
return 0;
} 执行结果:


首先在派生类D的对象d中只有基类的一个拷贝,因为改变x的值,输出时相同的;

其次,x的初值为0,虽然在执行D行时调用了类D的构造函数,而x的初值仍然为0,为什么呢?因为调用虚基类的构造函数方法与一般的构造函数不同。由于虚基类经过一次或多次派生出来的派生类,每一个派生类中的构造函数中都调用了基类的构造函数。在本例中,B和C都调用了A的构造函数对A的成员进行初始化,这样编译器就无法确定是用B的构函数还是C的构造函数来初始化A中的变量。于是,编译器调用A的默认构造函数来初始化A中的变量,也就是说A中必须有默认的构造函数,或者编译器将报错。

再次强调:用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则派生类的每一个派生类的构造函数的初始化成员列表中都必须有对虚基类构造函数的调用。


6.一些例子《面试宝典P123》

(1)范例1


[cpp]
#include

using namespace std;

class A{
protected:
int x;
public:
A(int a=0):x(a){};
int getData()const
{
return doGetData();
}
virtual int doGetData() const
{
return x;
}
};

class B:public A{
protected:
int x;
public:
B(int a=1):x(a){};
int doGetData()const
{
return x;
}
};

class C:public B{
protected:
int x;
public:
C(int a=2):x(a){};
};


int main()
{
C c(10); //1
cout< cout< cout< cout< cout< cout< cout< cout< system("pause");
return 0;
}

#include

using namespace std;

class A{
protected:
int x;
public:
A(int a=0):x(a){};
int getData()const
{
return doGetData();
}
virtual int doGetData() const
{
return x;
}
};

class B:public A{
protected:
int x;
public:
B(int a=1):x(a){};
int doGetData()const
{
return x;
}
};

class C:public B{
protected:
int x;
public:
C(int a=2):x(a){};
};


int main()
{
C c(10); //1
cout< cout< cout< cout< cout< cout< cout< cout< system("pause");
return 0;
}
运行结果 :1 1 1 1 1 0 1 1

解析:

//1:首先调用A类的构造函数,再调用B类构造函数,最后调用自己的构造函数。

//2:调用C中的getData,在C中未定义,于是调用基类B的getData,发现基类B也没有这个函数,继续往上走,则调用A中的getData。getData调用了虚函数doGetData,于是首先搜索C有没有定义这个函数,发现没定义,于是搜寻C的基类B有没有定义该虚函数,发现定义了,则调用B::doGetData。在B中的 doGetData返回的是B类的x,所以打印1。

p> //3:调用A::getData。后面分析同上红色地方。

//4:直接调用B::getData,然后B没有,转到B的基类A,所以调用A::getData。后面的分析同上红色的地方。


//5:同//2。

//6:调用C类的虚函数doGetData,C中未定义,于是就转到C类的基类B的goGetData,所以打印出1。

//7:直接调用A::doGetData,所以这次虚函数没有动态绑定了,直接打印出0。
//8:直接调用B::doGetData,所以这次虚函数没有动态绑定了,直接打印出1。

//9:直接调用 C::doGetData,但是C没有定义该虚函数,于是转到基类B,又打印出1。


注:这里有一个规则就是就近调用,及父辈如果有相关接口则调用父辈接口,父辈没有则转到祖父辈接口。

(2)范例2《面试宝典P125》


[cpp]
class A{
public:
virtual void fun();
}
class B:public A{
public:
virtual void fun();
}