一. 单基派生的情形:
对于
class base
{
};
class derived: public base //public inheritance
{
};
有以下赋值兼容规则可以遵循:
1. 派生类对象可以赋值给基类对象
但是,因为基类对象不具有派生类对象所具有的成员,因此基类对象不能赋值给派生类对象,强转也不行。
derived d;
base b;
b = d; // OK, 但是,会产生切割问题(即对象b不能访问对象d附加的功能)
d = b; // error
d = (derived)b; // error
2.派生类对象可以初始化基类的引用
derived d;
base& br=d; //OK
但是,如下语句却是错误的(原因同 "一.1"):
base b;
derived& d = b; // error: invalid initialization of reference of type 'derived&' from expression of type 'base'
3.
(1) 派生类对象的地址可以赋给指向基类的指针;
derived d;
base *pb=&d; //OK
这样,指向基类的指针,可以用来指向派生类对象中从基类继承下来的成员,但不包括派生类中追加的成员,除非采用强制类型转换(见例1:convert_test1.cpp);
而且,在这种情况下,调用的是基类的成员函数,除非派生类进行了虚函数继承(见例3:convert_test3.cpp)。
/* 例1:convert_test1.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
#include
class base
{
public:
base():i(0){ };
int get_i(){ return i; }
void set_i(int x){ i = x;}
void display_i(){ printf(" i = %d /n", i);}
private:
int i;
};
class derived: public base //public inheritance
{
public:
derived():j(0){ };
int get_j(){ return j; }
void set_j(int x){ j = x; }
void display_j(){ printf(" j = %d /n", j);}
private:
int j;
};
int main()
{
derived d;
base *pb=&d; //OK
pb->get_i();
//pb->get_j(); //compile error: F:/devcpptest/devcpptest/convert_test1.cpp 'class base' has no member named 'get_j'
//指向基类的指针,可以用来指向派生类对象中从基类继承下来的成员,但不包括派生类中追加的成员,除非对基类指针采用强制类型转换
base *pb2 = &d;
//pb2->set_j(4); //compile error: F:/devcpptest/devcpptest/convert_test1.cpp 'class base' has no member named 'set_j'
//(derived*)pb2->set_j(4); //compile error: F:/devcpptest/devcpptest/convert_test1.cpp 'class base' has no member named 'set_j'
((derived*)pb2)->set_j(4); //OK, 必须对基类指针进行强转,才可以访问派生类的成员
pb2->display_i();
((derived*)pb2)->display_j(); // OK,有必须对基类指针进行强转,才可以访问派生类的成员
while(1);
}
运行结果:
i = 0
j = 4
(2) 但是,基类指针不能直接赋值给派生类指针,得用强制类型转换:
base *pb = new b();
derived *pd = pb; // error: 基类指针不能直接赋值给派生类指针, 没有用强制类型转换
derived *pd = (derived*)pb; // OK: 基类指针直接赋值给派生类指针, 采用强制类型转换
具体的例子,见例2(convert_test2.cpp):
/* 例2:convert_test2.cpp */
/* 对于例1中的两个类base和derived,进行如下的测试: */
/* IDE环境: Dev-C++ 4.9.9.2 */
int main()
{
base *pb = new base;
//derived *pd = pb; // error: 基类指针不能直接赋值给派生类指针, 没有用强制类型转换
derived *pd = (derived*)pb; // OK: 基类指针不能直接赋值给派生类指针, 采用强制类型转换
pd->get_i();
pd->get_j();
pd->set_i(3);
pd->set_j(5);
pd->display_i();
pd->display_j();
while(1);
}
运行结果:
i = 3
j = 5
/* 例3:convert_test3.cpp */
/* IDE环境: Dev-C++ 4.9.9.2 */
/* 虚函数继承举例 */
#include
class base
{
public:
base():i(0){ };
int get_i(){ return i; }
void set_i(int x){ i = x;}
void virtual display_i(){ printf(" base::display_i, i = %d /n", i);} //虚函数
private:
int i;
};
class derived: public base //public inheritance
{
public:
derived():j(0){ };
int get_j(){ return j; }
void set_j(int x){ j = x; }
void display_j(){ printf(" j = %d /n", j);}
void display_i(){ printf(" derived::display_i, i = %d /n", get_i());}
private:
int j;
};
int main()
{
derived d;
base *pb=&d; //OK
pb->set_i(2); // 调用基类的set_i方法。
pb->display_i(); // 调用的是派生类的display_i方法,如果在基类中的display_i不定义成virtual,则调用的就是基类的display_i了。
while(1);
}
//运行结果:
derived::display_i, i = 2
二.多基派生:
1.对于如下的类层次结构,
class base0
{ protected : int b0 ;} ;
class base1: public base0
{ protected : int b1 ; } ;
class base2 : public base0
{ protected : int b2 ; } ;
class derived : public base1 , public base2
{
public :
int f ( ) ;
private : float d ;
} ;
可以:
(1) 隐含地把指向派生类的指针转换为指向基类的指针,如同单继承一样;
(2) 指向基类的指针强制类型转换为指向派生类的指针如同单继承一样;
然而,在这种情况下,会产生如下的问题,称作“多重继承中对基类成员访问的二义性”:
(1)当试图将指向派生类的指针(derived指针)转换为指向其间接公共基类指针(base0指针)时,C++编译器不知道是从哪一个路径(base1 还是 base2)继承,
从而导致编译错误,即使采用强制转换也不行。
(2) 同样,当派生类引用间接基类的成员时,由于有两份copy,分别来自base1 和 base2,所以编译器不知道引用的base0的成员是从哪里来的。
如:在display()中,如下语句是错误的: printf(" base0::b0 = %d /n", b0);
2. 克服这种二义性的方法:
(1) 对指针要显示地指明全路径。
(2) 将指针先强制转换到不会产生二义性的基类。
(3) 显示指明成员来自哪个类。
3. 程序举例:
/* multiinherencetest.cpp */
/* 多基派生 */
/* IDE环境: Dev-C++ 4.9.9.2 */
#include
class base0
{ protected : int b0 ;} ;
class base1: public base0
{ protected : int b1 ; } ;
class base2 : public base0
{ protected : int b2 ; } ;
class derived : public base1 , public base2
{
public :
int display( )
{
//printf(" base0::b0 = %d /n", b0); // error F:/devcpptest/devcpptest/multiinherencetest.cpp reference to `b0' is ambiguous
// error F:/devcpptest/devcpptest/multiinherencetest.cpp:4 candidates are: int base0::b0
printf(" base0::base1::b0 = %d /n", base1::b0); // OK
}
private : float d ;
} ;
int main()
{
derived d;
derived *pd = &d;
base1 *pb1;
base0 *pb;
pb1 = pd; // OK: 隐含地把指向派生类的指针转换为指向基类的指针。(和单继承一样)
pb1 = (base1*)pb; // OK:把指向基类的指针强制类型转换为指向派生类的指针。 (和单继承一样)
pb = pb1; //OK: 隐含地把指向派生类的指针转换为指向基类的指针。(和单继承一样)
pd = (derived*)pb1; //OK: 把指向基类的指针强制类型转换为指向派生类的指针。(和单继承一样)
//pd = pb; // compile error: F:/devcpptest/devcpptest/multiinherencetest.cpp invalid conversion from `base0*' to `derived*'
//pd = (derived*)pb; // compile error: F:/devcpptest/devcpptest/multiinherencetest.cpp `base0' is an ambiguous base of `derived'
//pb = pd; // compile error: F:/devcpptest/devcpptest/multiinherencetest.cpp `base0' is an ambiguous base of `derived'
//pb = (base0*)pd; // compile error: F:/devcpptest/devcpptest/multiinherencetest.cpp `base0' is an ambiguous base of `derived'
//用如下形式消除以上的错误:
pb = (base0*)(base1*) pd; //OK
pd = (derived*)(base1*)pb; //OK
pb = (base1*) pd; //OK
while(1);
return 0;
}
三. 含有公共虚基类的类层次结构:
使用虚基类,在它的几条派生路径的汇合处,只产生一个copy,所以,就不会产生二义性。 对于公共虚基类的类层次结构,
可以:
1. 派生类对象的地址可以直接赋给间接公共基类的指针,并且不需要进行强制类型转换;如,
base0 *pb = &d // OK
2. 一个虚基类的引用,可以引用一个派生类的对象。如,
base0 &rb = d; // OK
但是:
相反的转换是不可以的,即使使用指定路径的强制转换也不行。如,
derived *pd = (derived*)(base1*)pb; // compiler error
因为系统进行内存分配时,虚基类中的数据成员在派生类对象中的布局与非虚基类时不同,所以不能将指向虚基类的指针(或引用)置回指向派生类。
3. 程序举例:
/* multiinherencetest2.cpp */
/* 含有虚基类的多基派生 */
/* IDE环境: Dev-C++ 4.9.9.2 */
#include
class base0
{ protected : int b0 ;} ;
class base1: public virtual base0
{ protected : int b1 ; } ;
class base2 : public virtual base0
{ protected : int b2 ; } ;
class derived : public base1 , public base2
{
public :
int display( )
{
//printf(" base0::b0 = %d /n", b0); // error F:/devcpptest/devcpptest/multiinherencetest2.cpp reference to `b0' is ambiguous
// error F:/devcpptest/devcpptest/multiinherencetest2.cpp:4 candidates are: int base0::b0
printf(" base0::base1::b0 = %d /n", base1::b0); // OK
}
private : float d ;
} ;
int main()
{
derived d;
base0 *pb = &d; // OK
base0 &rb = d; // OK
//derived *pd = (derived*)(base1*)pb; //compile error: F:/devcpptest/devcpptest/multiinherencetest2.cpp cannot convert from base `base0' to derived type `base1' via virtual base `base0'
while(1);
return 0;
}