1.定义
class c:a,b {
//…
};
加上访问控制其形式可为:
class c:punlic a,b { //c类对a类是公有派生,对b类私有派生
//…
};
或
class c:a,public b {
//…
};
class c:public a,public b {
//…
};
例:x,y,z三个类的例子z是x,y的派生类,在派生时,对于x类为公有派生,对于y类为私有派生。这样对于基类成员在派生类中可访问性的不同,处理的方式也就不同。
#include<iostream>
using namespace std;
class x
{
private:
int a;
public:
void seta(int xa)
{
a = xa;
}
void showa()
{
cout << "a = " << a << endl;
}
};
class y
{
private:
int b;
public:
void setb(int yb)
{
b = yb;
}
void showb()
{
cout << "b = " << b << endl;
}
};
class z :public x, y//对x类公有派生,对y类私有派生
{
private:
int c;
public:
void setbc(int zc, int zb)
{
c = zc;
setb(zb);
}
void show()
{
showa();
showb();
cout << "c = " << c << endl;
}
};
int main()
{
z obj;
obj.seta(11);//给a赋值
obj.setbc(21, 22);//给b,c赋值
obj.show();//显示a,b,c
obj.showa();//显示a
//obj.showb();//显示b,在此不能调用此函数,因为z对y是私有派生
getchar();
}
/*
a = 11
b = 21
c = 22
a = 11
*/
2.多继承的构造函数和析构函数
多继承的构造函数的定义与单继承的相似,只是几个基类的构造函数之间用“,”分隔。class windows{ //定义窗口类
//…
public:
windows(int top,int left,int botcom,int right);
~window();
};
class scrollbar{ //定义滚动条
//…
public:
scrollbar(int top,int left,int bottom,int right);
~scrollbar();
};
class scrollbarwind : windows ,scrollbar{//定义派生类
//…
public:
scrollbarwind(int top,int left,int bottom,int right);
~scrollborwind();
};
scrollbarwind::scrollbarwind(int top,int left,int bottom,int right):window(top,left,bottom,
right):scrollbar(top,left,bottom,right)//缀上基类的构造函数
{//…
}
(2)构造函数和析构函数的执行顺序多继承的构造函数的调用顺序与单继承的相同,也是遵从先祖先(基类),再客人(成员对象),后自己(派生类)的原则。在多个基类之间则严格按照派生定义时从左到右的顺序来排列先后。析构函数的调用顺序则刚好与构造函数的相反。例如:
#include<iostream>
using namespace std;
class x
{
public:
x() { cout << "the constructor of x!\n"; }
~x() { cout << "the destructor of x!\n"; getchar();}
};
class y
{
public:
y() { cout << "the constructor of y!\n"; }
~y() { cout << "the destructor of y!\n"; }
};
class z :public x, public y
{
public:
int x;
z() { cout << "the constructor of z!\n"; }
~z() { cout << "the destructor of z!\n"; }
};
int main()
{
z obj;
//getchar();//getchar加在这里会造成析构函数不调用,选择将其加在析构函数中或用codeblocks运行
}
/*
the constructor of x
the constructor of y
the constructor of z
the destructor of z
the destructor of y
the destructor of x
*/
(3)二义性问题
由于多继承情况下,可能造成对基类中的某个成员的访问出现不唯一的情况,称为对基类成员访问的二义性问题。
#include<iostream>
using namespace std;
class A
{
private:
int a;
public:
void f() { a = 5; }
};
class B
{
private:
int b;
public:
void f() { b = 1; }
void g() { b = 3; }
};
class C:public A,public B
{
private:
int c;
public:
void g() { c = 2; }
};
int main()
{
C obj;
obj.A::f();//用A的f()
obj.B::f();//用B的f()
obj.g();//隐含用C的g()
//obj.f();//错,存在二义性,无法确定是A中的还是B中的f()
getchar();
}
使用基类名可避免这种二义:
obj.A::f();//A中的f();
obj.B::f();//B中的f();
以上这种用基类名来控制成员访问的规则称为支配原则。
例如:
obj.g();//隐含用C的g()
obj.B::g();//用B的g()
以上两个语句是无二义的。
成员的访问权限不能区分有二义性的同名成员:
class A {
public:
void fun();
};
class B {
protected;
void fun();
};
class C : public A, public B { };
虽然类C中的两个fun()函数,一个公有,一个私有,但:
C obj;
obj.fun(); //错误,仍是二义的
在多继承情况下,造成的对基类中某个成员的访问出现的不唯一的情况:
当一个派生类从多个基类派生,而这些基类又有一个共同的基类,则对该基类中说明的成员进行访问时,可能会出现二义性,例:
#include<iostream>
using namespace std;
class x
{
protected:
int a;
public:
x() { a = 10; }
};
class y:public x
{
public:
y() { cout << a << endl; }
};
class z:public x
{
public:
z() { cout << a << endl; }
};
class k :y, z
{
public:
k() { cout << y::a << endl; }
//k() { cout << a << endl; }//错,应该为y::a或z::a
};
int main()
{
k obj;
getchar();
}
/*
10
10
10
*/
引入目的:用于有共同基类的场合。
为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝。若类D的两个直接基类都是从类A派生而来的,创建D的一个对象时将包含公共基类A的两个副本,在多数情况下只需要使用这两个副本中的任何一个,在直接派生类中将此公共基类声明为虚基类,然后创建类对象时只建立公共基类的一个副本。
#include<iostream>
using namespace std;
class x
{
protected:
int a;
public:
x() { a = 10; }
};
class y:virtual public x
{
public:
y() { cout << "a in y:" << a++ << endl; }
};
class z:virtual public x
{
public:
z() { cout << "a in z:" << a++ << endl; }
};
class k :y, z
{
public:
k() { cout << "a in k:" << a << endl; }
//k() { cout << a << endl; }//错,应该为y::a或z::a
};
int main()
{
k obj;
getchar();
}
/*
a in y:10
a in z:11
a in k:12
*/
例:
#include<iostream>
using namespace std;
class Base0
{
public:
int var0;
void fun0()
{
cout << "Base0\n";
}
};
class Base1:virtual public Base0//派生类Base1
{
public:
int var1;
};
class Base2:virtual public Base0//派生类Base2
{
public:
int var2;
};
class Derived:public Base1, public Base2
{
public:
int var;
void fun(){cout << "Derived!\n";}
};
int main()
{
Derived d1;
d1.var0 = 0;//直接访问虚基类的数据成员
d1.fun0();//直接访问虚基类的函数成员
}
虚拟继承内存布局
虚基类的初始化
虚基类的初始化与一般多继承的初始化在语法上是一样的,但构造函数的调用顺序不同。
派生类构造函数调用次序:
虚基类的构造函数在非虚基类之前调用;
若同一层次中包含多个非虚基类,这些非虚基类的构造函数按它们的说明次序调用;
若虚基类由非虚基类派生而来,则仍然先调用虚基类的构造函数,再调用派生类的构造函数。
//codeblock
#include <iostream>
using namespace std;
class base1
{
public:
base1(){cout << "base1" << endl;}
};
class base2
{
public:
base2(){cout << "base2" << endl;}
};
class level1:public base2, virtual public base1
{
public:
level1(){cout << "level1" << endl;}
};
class level2:public base2, virtual public base1
{
public:
level2(){cout << "level2" << endl;}
};
class toplevel:public level2, virtual public level1
{
public:
toplevel(){cout << "toplevel" << endl;}
};
int main()
{
toplevel obj;
return 1;
}
/*
base1
base2
level1
base2
level2
toplevel
*/
虚基类的构造函数
为了初始化基类的子对象,派生类的构造函数要调用基类的构造函数。对于虚基类来讲,由于派生类的对象中只有一个虚基类子对象。为保证虚基类子对象只被初始化一次,这个虚基类构造函数必须只被调用一次。由于继承结构的层次可能很深,规定将在建立对象时所指定的类称为最直接派生类。
虚基类子对象是由最直接派生类的构造函数通过调用虚基类的构造函数进行初始化的。
如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用,如果未被列出,则表示使用该虚基类的默认构造函数来初始化派生类对象中的虚基类子对象。
小结:
<1>基类和派生类体现了类之间的层次关系,是对现实世界客观的反映,派生类是对软件重用的手段之一;
<2>有三种派生类型:公有\保护\和私有.不同的派生类型对基类成员的访问规则不同;
<3>派生类的构造函数自动执行基类的构造函数,且基类的构造函数先执行,基类构造函数的参数由派生类传递;
<4>派生类型具有基类的所有成员,并可对已有的成员 进行重新定义.在派生类中访问基类的函数用::表示;
<5>可定义多重派生类,且可利用虚基类使派生类只保持一个基类的成员拷贝。