多继承定义:从多个基类中派生的继承称为多继承,多继承的派生类则有多个基类。
多继承派生类的定义格式:
class <派生类名> ;<继承方式1> <基类名1> , <继承方式2> <基类名2>,...
{
<派生类新成员定义>
};
与单继承派生类的定义格式相比较,可以看出,它与单继承派生类在定义格式上的区别仅在于它的基类名多,各基类名之间用逗号分隔,每个基类名前都应有一个该基类的继承方式的说明 。
单继承中的许多规则都适用于多继承。由于多继承是由多个基类继承的,因此在定义多继承的派生类时,要指出它的所有基类名及继承方式。
在多继承的情况下,派生类的构造函数的定义格式为:
<派生类名> ( <总参数表> ) : <基类名1> ( <参数表1> ), <基类名2> ( <参数表2> ) ...
{
<派生类构造函体>
}
将它与单继承派生类构造函数进行比较,可以发现,它们的区别仅在于冒号后面调用的基类的构造函数多了,这是因为它的基类多了。这里的总参数表中的参数包括冒号后面所有参数表中参数的总和。
多继承派生类构造函数执行顺序与单继承派生类构造函数执行顺序相似,先执行所有基类的构造函数,再执行派生类本身的构造函数。这里需要指出的是,多个基类构造函数的执行顺序取决于定义派生类时所指定的各个基类的顺序,而与派生类构造函数的成员初始化列表中给定的基类顺序无关。
下面就来用例子来说明多继承:
<span style="font-size:18px;">#include <iostream>
using namespace std;
class A1
{
public:
A1(int i)
{
a1=i;
cout<<"Constructor A1."<<a1<<endl;
}
void Print()
{ cout<<a1<<endl; }
private:
int a1;
};
class A2
{
public:
A2(int j)
{
a2=j;
cout<<"Constructor A2."<<a2<<endl;
}
void Print()
{ cout<<a2<<endl; }
private:
int a2;
};
class A3
{
public:
A3(int k)
{
a3=k;
cout<<"Constructor A3."<<a3<<endl;
}
int Geta3()
{ return a3; }
private:
int a3;
};
class D:public A1,public A2//派生类D公有继承两个基类A1和A2
{
public:
D(int i,int j,int k,int l):A2(i),A1(j),a3(k)//派生类的构造函数,
{
d=l;
cout<<"Constructor D."<<d<<endl;
}
void Print()
{
A1::Print();//说明基类A1的成员函数
A2::Print();//说明基类A2的成员函数,旨在消除二义性
cout<<d<<","<<a3.Geta3()<<endl;//通过对象a3调用基类A3中的成员函数
}
private:
int d;
A3 a3;//子对象
};
int main()
{
D dd(6,7,8,9);//派生类D的对象dd
dd.Print();
A2 a2(4);
a2=dd;//把对象dd赋值给基类A2的对象a2
a2.Print();
A1 a1(2);
a1=dd;//把对象dd赋值给基类A1的对象a1
a1.Print();
return 0;
}
</span>
程序分析:
1,在多继承派生类D的构造函数调用前先调用基类A1的构造函数,再调用基类A2的构造函数,然后再调用自对象类A3的构造函数,最后调用多继承派生类D的构造函数,这个顺序取决于定义派生类D时所给出的基类顺序,基类A1在前,基类A2在后,而与定义派生类构造函数成员初始化列表中的给定的基类顺序无关。
2,在派生类D中定义了成员函数Print(),在它的函数体内有两条语句: A1::Print();和A2::Print();这里是通过作用域运算符::来解决到底调用哪个Print()函数。
3,在主函数中,定义的基类A1的对象和基类A2的对象,并将派生类D的对象赋值给它们,这是因为派生类D与基类A1和基类A2时类型适应的,所以这种赋值才是正确的。
多继承中的二义性问题归纳:
1,调用不同基类的相同成员时可能出现二义性
2,访问共同基类的成员时可能出现二义性,当一个派生类有多个基类,而这些基类中又有一个公有基类,这时对这个共同基类中成员的访问可能出现二义性。
<span style="font-size:18px;">#include <iostream>
using namespace std;
class A
{
public:
A(int i)
{
a=i;
cout<<"Constructor called.A\n";
}
void Print()
{ cout<<a<<","; }
~A()
{ cout<<"Destructor called.A\n"; }
private:
int a;
};
class B1:public A
{
public:
B1(int i,int j):A(i)
{
b1=j;
cout<<"Constructor called.B1\n";
}
void Print()
{
A::Print();//说明基类A中的成员函数
cout<<b1<<",";
}
~B1()
{ cout<<"Destructor called.B1\n"; }
private:
int b1;
};
class B2:public A
{
public:
B2(int i,int j):A(i)
{
b2=j;
cout<<"Constructor called.B2\n";
}
void Print()
{
A::Print();//说明基类A中的成员函数
cout<<b2<<",";
}
~B2()
{ cout<<"Destructor called.B2\n"; }
private:
int b2;
};
class C:public B1,public B2//多继承派生类都公有继承基类B1和基类B2
{
public:
C(int i,int j,int k,int l,int m):B1(i,j),B2(k,l)//多继承派生类C的构造函数
{
c=m;
cout<<"Constructor called.C\n";
}
void Print()
{
B1::Print();//说明基类B1的成员函数
B2::Print();//说明基类B2的成员函数,旨在消除二义性
cout<<c<<endl;
}
~C()
{ cout<<"Destructor called.C\n"; }
private:
int c;
};
int main()
{
C c1(6,9,12,25,38);
c1.Print();
return 0;
}</span>
程序分析:
1,在创建多继承派生类C的对象c1时,先调用类A的构造函数,再调用类B1的构造函数,再调用类A的构造函数,然后调用类B2的构造函数,最后调用多继承派生来C的构造函数。
2,通过派生类中的成员函数输出五个数字,最后依次与调用构造函数相反的顺序调用析构函数释放对象。