最近在写跨服组队的功能,需要重构组队匹配的代码,在开发的过程中,遇到一个父类函数中的某一步调用需要被子类重写的问题,为了重写的函数能被正确调用,做了一下实验,结果如下:
class A{
public:
void Foo();
void Foo0();
virtual void Foo1();
void Foo2();
};
void A::Foo(){
Foo0();
Foo1();
}
void A::Foo0(){
cout<<"aaa000\n";
}
void A::Foo1(){
cout<<"aaa111\n";
}
void A::Foo2(){
cout<<"aaa222\n";
}
class B:public A{
public:
void Foo0();
virtual void Foo1();
void Foo2();
};
void B::Foo0(){
cout<<"bbb000\n";
}
void B::Foo1(){
cout<<"bbb111\n";
}
void B::Foo2(){
cout<<"bbb222\n";
}
int main(){
A tmp_a;
B tmp_b;
tmp_a.Foo();///aaa000 aaa111
tmp_b.Foo();///aaa000 bbb111
/*子类调用未重写的父类函数Foo,Foo继续调用子类已重写的函数:
若该函数是普通函数(Foo0),则仍然调用父类的实现;若该函数是虚函数(Foo1),则调用子类的实现*/
tmp_a.Foo2();///aaa222
tmp_b.Foo2();///bbb222
tmp_a = tmp_b;
tmp_a.Foo();///aaa000 aaa111
/*当我们用子类的对象给基类的对象赋值或者初始化基类的对象时,
它的本质就是将从父类继承来的数据成员的值赋给父类的对象,
而此时子类对象中的其他数据将会被截断,也就是丢失*/
A* p_a = new B;
p_a->Foo();///aaa000 bbb111
/*如果用父类指针指向子类对象,
那么父类指针也只能访问到父类所拥有的数据成员和成员函数,
而无法访问到子类所独有的数据成员和成员函数。*/
A& iter_a = tmp_b;
iter_a.Foo();///aaa000 bbb111
/*引用和指针类似*/
return 0;
}
总结:
1.子类调用未重写的父类函数Foo,Foo继续调用子类已重写的函数:若该函数是普通函数(Foo0),则仍然调用父类的实现;若该函数是虚函数(Foo1),则调用子类的实现
2.当我们用子类的对象给基类的对象赋值或者初始化基类的对象时,它的本质就是将从父类继承来的数据成员的值赋给父类的对象,而此时子类对象中的其他数据将会被截断,也就是丢失
3.如果用父类指针指向子类对象,那么父类指针也只能访问到父类所拥有的数据成员和成员函数,而无法访问到子类所独有的数据成员和成员函数。
4.另外注意一下基类指针的回收问题。当用基类指针指向子类从堆中分配的对象时,如下形式 A*p = new B;当调用delete p;p=NULL;销毁对象时,是调用父类A的析构函数还是调用子类B的构造函数呢?如下图所示:答案是会调用父类的构造函数,这样问题就来了,子类不是从父类继承来的那些独有的成员变量的内存将得不到释放,将会造成内存泄露,这种情况应该如何避免内存泄露呢?这就需要使用虚析构函数。
虚析构函数就是在父类的析构函数前加上virtual关键字,这种特性将会被继承下去,也即子类的析构函数也为虚析构函数
虚析构函数的使用场合:当存在继承关系,用父类的指针指向从堆中分配的子类的对象时,然后又想用父类的指针去释放掉内存,就会用到虚析构函数,用了虚析构函数后,就会先调用子类的析构函数,再调用父类的构造函数了
网友评论