类的继承分为3类:单继承,多继承和虚继承。
1. 单继承
class B {
public:
int b1;
B(int init) {
b1 = init;
}
};
class D : public B1 {
public:
int d1;
D(int init)
: B1(init - 1)
{
d1 = init;
}
};
int main()
{
D d(5);
}
这时在内存中可以看到先分配了Base类的空间,这里是4B。关于类大小的计算执行百度。
2. 多继承
class B1 {
public:
int b1;
B1(int init) {
b1 = init;
}
};
class B2 {
public:
int b2;
B2(int init) {
b2 = init;
}
};
class D : public B1, public B2 {
public:
int d1;
D(int init)
: B1(init - 1)
, B2(init - 2)
{
d1 = init;
}
};
这时的内存图为:
现在单继承和多继承有点了解了,那么如果多继承中的2个基类 B1, B2 又同时继承于类 A,这时内存是如何表现的。
class A {
public:
int a1;
A(int init) { a1 = init; }
};
class B1 : public A { ... };
class B2 : public A { ... };
/* 这里的流程如上面的多继承 */
内存图:
这样的静态分配看出没有任何的问题,我们使用指针去访问类A的变量a1。
D d(5);
// A *p = (A *)&d; // 语法错误
B1 *p1 = (B1 *)&d;
A *p = (A *)p1;
cout << p->a1 << endl; // 3
B2 *p2 = (B2 *)&d;
p = (A *)p2;
cout << p->a1 << endl; // 2
// d.a1 = 1; // 这时会报错,因为这个时候不知道指定的a1变量是哪个。
擦,这个编译器还是可以根据对应的继承关系找到对应的值的。叼,这里有个问题,因为现在我们都带了初始化的变量,这时从B1, B2分别实例化的A具有不同的值,但是如果我们想想这种情形。如果类A中是一些需要实现的方法,按这种方式,我们需要分别在 B1 , B2中分别去实现 A 的方法,如果需要实现的类方法是有差异的,那么这样的做法就是正确的。但是如果我们需要实现的类方法是一样的,那么这里A的每个方法在内存中就有重复的2个版本,这样就造成了空间的浪费。这里可以使用虚继承。
3. 虚函数
class B {
public:
int b;
B(int init) { b = init; }
void Print() { cout << b << endl; }
virtual void vPrint() { cout << b << endl; }
};
class D : public B {
public:
int d;
D(int init):B(init-1) { d = init; }
void Print() { cout << d << endl; }
virtual void vPrint() { cout << d << endl; }
};
int main()
{
D d(5);
B *pb = (B *)&d; // 使用基类指针
d.Print(); // 5
pb->Print(); // 4
pb->vPrint(); // 5
}
内存图:
可以看到,类D的实例化变量 d 的大小为12,只包含了 本身和基类的变量和一个虚函数指针的地址。这里应该称为虚函数表的地址指针。
虚函数在继承的时候会发生覆盖,这从程序的执行结果和内存图中都可以看到。而对于类中的一般函数,则没有覆盖现象的发生。
如果在基类中我们只定义一些函数,而不去实现它,这时就需要使用到纯函数,这种一般都是让这些函数在派生类中去实现,这样可以称为接口。
virtual void Print()=0; // 纯虚函数定义 virtual [返回类型] [函数名]([变量列表]) = 0;
当类中又这样的纯虚函数定义存在的时候,这时的类称为抽象类,在语法上是不允许被实例化的。因为其中的函数需要派生类去实现。
4. 虚继承
class A {
public:
int a;
A() { a = 3; }
};
class B1 : virtual public A {
public:
int b1;
B1() { b1 = 4; }
};
class B2 : virtual public A {
public:
int b2;
B2() { b2 = 5; }
};
class C : virtual public B1, virrual public B2 {
public:
int c;
C() { c = 6; }
};
int main()
{
C c;
return 0;
}
内存图:
内存的分配还是有点不清楚。