多继承声明
在C++中,声明具有两个以上基类的派生类与声明单继承派生类的形式相似,只需将要继承的多个基类使用逗号“,”分隔即可。其声明的一般形式如下:
class 派生类名 : 继承方式1 基类名1,继承方式2 基类名2,……,继承方式n 基类名n
{
// 派生类新增数据成员和成员函数
};
其中,继承方式1、继承方式2、……继承方式n是3种继承方式public、private和protected之一。缺省的继承方式是private,即没有说明继承方式的基类名,都是指private方式。
例:多继承时派生类的成员访问属性。
#include <iostream.h>
class A
{
private:
int x;
public:
void setA(int i)
{
x = i;
}
void showA()
{
cout << "x=" << x << endl;
}
};
class B
{
private:
int y;
public:
void setB(int j)
{
y = j;
}
void showB()
{
cout << "y=" << y << endl;
}
};
class C : public A, private B
{
private:
int z;
public:
void setC(int i, int j, int k)
{
z = k;
setA(i);
setB(j);
}
void showC()
{
showA();
showB();
cout << "z=" << z << endl;
}
};
void main()
{
C obj;
obj.setA(1);
obj.showA();
obj.setC(1,2,3);
obj.showC ();
//obj.setB(5); // 非法,setB()在派生类C中是private成员
//obj.showB (); // 非法,showB()在派生类C中是private成员
}
执行结果:
x=1
x=1
y=2
z=3
多继承的构造函数和析构函数
多继承派生类的构造函数形式与单继承构造函数的定义形式基本相同,只是在初始化表中包含多个基类构造函数,它们之间用“,”隔开。多继承构造函数定义的一般形式如下:
派生类名(总参数列表):基类名1(参数表),基类名2(参数表),……,基类名n(参数表)
{
// 派生类新增成员初始化语句
}
多继承下派生类构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数个数必须包含完成所有基类初始化所需的参数个数。
多重继承的构造函数的执行顺序与单继承构造函数执行顺序相同,也是遵循先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类构造函数的原则。处于同一层次的各个基类构造函数执行顺序,取决于声明派生类时所指定的各个基类的顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序没有关系。析构函数的执行顺序刚好与构造函数的执行顺序相反。
例:多继承时派生类的构造函数与析构函数。
#include <iostream.h>
class A1
{
private:
int a1;
public:
A1(int i)
{
a1 = i;
cout << "A1 Constructor:" << a1 << endl;
}
~A1()
{
cout << "A1 Destructor" << endl;
}
};
class A2
{
private:
int a2;
public:
A2(int j)
{
a2 = j;
cout << "A2 Constructor:" << a2 << endl;
}
~A2()
{
cout << "A2 Destructor" << endl;
}
};
class A3
{
private:
int a3;
public:
A3(int k)
{
a3 = k;
cout << "A3 Constructor:" << a3 << endl;
}
~A3()
{
cout << "A3 Destructor" << endl;
}
};
class D : public A1, public A2
{
private:
int d;
A3 obj;
public:
D(int i, int j, int k, int l) : A2(i), A1(j), obj(k)
{
d = l;
cout << "D Constructor:" << d << endl;
}
~D()
{
cout << "D Destructor" << endl;
}
};
void main()
{
D dd(1, 2, 3, 4);
}
执行结果:
A1 Constructor:2
A2 Constructor:1
A3 Constructor:3
D Constructor:4
D Destructor
A3 Destructor
A2 Destructor
A1 Destructor
派生类重载基类成员和二义性问题
派生类的成员包括从基类继承来的和自己新增加的成员。由于派生类可以看成是基类的一个特殊情况,所以在内部处理上可能也会与基类有所不同。可以对基类继承来的成员加以重新定义,使得它们虽然与基类成员拥有相同的名称,却可以处理各自特殊的情况。这种派生类与基类有同名成员的情况,就是派生类重载基类成员。
例:派生类重载基类成员。
#include <iostream.h>
class animal
{
public:
void call()
{
cout << "o...!o...!" << endl;
}
void eat()
{
cout << "I eat food." << endl;
}
};
class dog: public animal
{
public:
void call()
{
cout << "won won" << endl;
}
};
class cat: public animal
{
public:
void call()
{
cout << "meow meow" << endl;
}
};
void main()
{
animal a;
dog b;
cat c;
a.call ();
b.call ();
c.call ();
b.animal::call ();
c.animal::call ();
b.eat();
c.eat ();
}
执行结果:
o...!o...!
won won
meow meow
o...!o...!
o...!o...!
I eat food.
I eat food.
说明:
① 经过继承所得成员,如果派生类没有重新定义,那么它会使用基类的成员;
② 如果派生类重载基类成员,那么就会掩盖基类的同名成员,改用派生类的新版本。如果要使用基类名,需要采用以下形式:
基类名::成员名
多继承时,如果不同基类中有同名成员,则在派生类中就会有同名成员,这种情况就会造成二义性。
1. 成员函数的二义性问题
如果一个派生类从多个基类继承而来,可能存在成员函数的二义性问题。
例:多继承中成员函数的二义性问题。
#include <iostream.h>
class A
{
protected:
int i;
public:
void show()
{
cout << "i=" << i << endl;
}
};
class B
{
protected:
int j;
public:
void show()
{
cout << "j=" << j << endl;
}
};
class C : public A, public B
{
public:
void set(int x, int y)
{
i = x;
j = y;
}
};
void main()
{
C obj;
obj.set(1, 2);
obj.A::show ();
obj.B::show ();
}
派生类C的两个基类A和B都有show()函数,因此在C类中有两个同名的show()函数,如果以obj.show()的形式访问show()函数,会产生二义性错误。使用基类名可避免相同的基类名称,避免这种二义性。
2. 成员变量的二义性问题
虽然C++语言不允许一个类被多次声明为同一派生类的直接基类,但允许一个类被多次声明为同一个派生类的间接基类。被两次或两次以上声明为某个派生类间接基类的基类,称为该派生类的公共基类。
多重继承时存在公共基类的现象,使派生类的成员变量有可能存在二义性问题。
例:公共基类引起的二义性问题。
#include <iostream.h>
class A
{
public:
int b;
void display()
{
cout << "Display A" << endl;
}
};
class B : public A
{
public:
int b1;
};
class C : public A
{
public:
int b2;
};
class D : public B, public C
{
public:
int d;
};
void main()
{
D obj;
//obj.b = 2; // 二义性错误,编译器无法确定是哪一个b
obj.C::b = 5;
obj.B::b = 6;
obj.b1 = 7;
obj.b2 = 8;
obj.d = 9;
// obj.display (); // 同样是二义性错误
obj.C::display ();
obj.B::display ();
}
实际上,大多数程序设计中不希望出现上述成员变量的二义性问题。要想在多重继承时不出现多个同名的成员变量,就要在定义派生类时使用下一节讨论的虚基类方法。