覆盖
覆盖就是子类会覆盖与父类相同的虚函数,下来看一个覆盖的代码:
class A
{
public:
void virtual f()
{
cout << "A" << endl;
}
};
class B :public A
{
public:
void virtual f()
{
cout << "B" << endl;
}
};
int main()
{
A* pa = new A();
pa->f(); //A
B* pb = (B*)pa;
pa->f(); // A
delete pa, pb;
pa = new B();
pa->f(); //B
pb = (B*)pa;
pb->f(); //B
return 0;
}
这是一个虚函数覆盖虚函数的问题,A中f是一个虚函数,虚函数是被子类同名函数所覆盖的。送一A类中的f函数会被B类中的f函数覆盖。很明显第一个输出是输出“A”,但是在 B* pb = (B*)pa;里面,该语句的意思是转化pa为B类型并新建一个pb指针,将pa赋值到pb,所以pa在这里是没有发生任何变化的,所以第二个pa->f也指向的是“A”。
delete pa,pb删除了pa,pb指向的地址,但pa,pb指针并没有被删除,这就是野指针(悬浮指针),现在重新指向B类,而pa指针类型是A类的,父类的指针指向子类的对象,在这里就会发生覆盖。第三个打印的是“B”.pb=(B*)pa;转化pa为B类指针给pb赋值,但pa所指向的f函数是B类的f函数,所以pb所指向的f函数是B类的f函数,打印“B”。
虚继承
虚继承是多重继承中特有的概念,虚基类是为了解决多重继承而出现的。例如B继承A,C继承A,D同时继承BC,因此出现了交叉继承,在D类中会出现两个A,为了节省空间,可以将B,C对A的继承定义为虚拟继承,而A就成了虚基类,这就是所谓的菱形继承。
class A;
class B :public virtual A;
class C :public virtual A;
class D :public B, public C;
虚函数继承
下面看一段代码:
class A
{
public:
virtual void a()
{}
};
class B :public virtual A
{
public:
virtual void b()
{}
};
class C:public virtual A
{
public:
virtual void c()
{}
};
int main()
{
cout << sizeof(A) << endl; // 4
cout << sizeof(B) << endl; // 12
cout << sizeof(C) << endl; // 20
return 0;
}
A类中有一个虚函数,所以必须有一个对应的虚函数表来记录对应的函数入地址。所以有虚函数的类生成对象的前四个字节是一个指针,指向的是虚函数生成的虚函数表,所以A类的大小是4个字节。
对于B类,虚继承了A类,同时还拥有自己的系函数,那么B类有一个B类vfptr指针指向自己的虚函数表,这里大小是4个字节。可虚继承需要通过加入一个虚类指针记为vfptr_B_A来指向父类,这里大小也是4个字节,同时还有继承父类的所有内容sizeof(A)是4个字节大小,所以B类的大小是12个字节。
C类的话也有一个自己的vfptr_C指向自己的虚函数表,然后是一个虚类指针vfptr_C_B_A,同时还继承了B类的所有内容,所以大小是12字节+4+4 = 20字节。
这就是虚函数继承,虚继承不仅继承了所有父类内容,还有一个虚类指针来指向父类。这里的大小是在gcc编译下的结果,如果父类有一个指向自己虚函数的指针,会和子类共享这个父类的虚函数表的空间,不会占用子类的内存。在VC条件下编译不共享这个虚函数指针空间
纯虚函数
看下面代码有什么问题?
class A
{
public:
A()
{}
~A()
{}
virtual void f() = 0;
};
int main()
{
A a;
return 0;
}
这个代码的问题出在A a;这里,因为A类中的f函数是一个纯虚函数,所以A类不能实例化对象。解决方法是将f()函数修改成一般函数。