上篇文章简要讲了多态场景下的一个坑,今天对父类指针和子类指针分别指向一个子类对象的访问权限以及调用析构函数问题。
class A
{
public:
virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
virtual void test(){ func();}
};
class B : public A
{
public:
void func(int val = 0){ std::cout<<"B->"<< val <<std::endl; }
};
int main(int argc ,char* argv[])
{
B*p1 = new B;
A*p2 = new B;
p1->test();
p2->test();
return 0;
}
这里不管是p1还是p2调用test()函数,最终结果都是B->1,但是运行机制不一样。先分析p1->test(),p1->test() 是通过派生类指针调用,直接访问 A::test(),又因为B重写了func()函数,会调用 B::func()(动态绑定),不过默认参数的值还是由 A::func() 决定(静态绑定),主要原因还是因为A的func()是虚函数; p2->test()的分析差别在于p2是父类指针,加上虚函数满足了多态的条件,而p1是派生类指针,不满足多态。
是否可以理解成 p1 和 p2 都指向一个 B 类对象,可以访问 B 类的全部内容?
p1 和 p2 都指向一个 B 类对象,这是正确的,所以在有虚函数func()时才能都调用B的func()。
但是p1(B* 类型):可以访问 B 类的全部内容(包括从 A 继承的部分和 B 自己定义的部分);而p2(A* 类型):只能访问 A 类的成员和 B 类重写的虚函数,不能直接访问 B 类的特有成员。
对代码进行修改,说明虚函数对析构的影响。
class A
{
public:
virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
virtual void test(){ func();}
~A()
{
cout<<"~A()"<<endl;
}
};
class B : public A
{
public:
void func(int val = 0){ std::cout<<"B->"<< val <<std::endl; }
~B()
{
delete arr;
cout<<"~B()"<<endl;
}
private:
int* arr=new int[10];
};
int main(int argc ,char* argv[])
{
B*p1 = new B;
A*p2 = new B;
// p1->test();
// p2->test();
delete p1;
delete p2;
return 0;
}
对于delete p1,显然是先调用派生类析构函数再调用基类析构函数;但是对于delete p2,由于p2是A*,只能访问到派生类B的基类部分,所以只会调用~A(),没有调用B的析构函数,且~B() 存在资源释放,此时就会造成内存泄漏。当给~A() 前加上virtual时,无论派生类B的析构函数是否加关键字,都与基类的析构函数构成重写,虽然基类和派生类析构函数名不同看起来不符合重写的规则,实际上是编译器对析构函数名称做了特殊处理,编译后析构函数的名称统一为destructor,此时就不存在资源泄漏问题。
////简单总结就是
如果基类的析构函数不是虚函数,通过基类指针删除派生类对象时,会导致派生类的资源泄漏。将基类的析构函数声明为虚函数可以解决这个问题。
析构函数的重写:只要基类的析构函数是虚函数,派生类的析构函数就会自动成为虚函数(即使没有显式声明 virtual),不仅是析构函数,其他只要是基类函数有virtual,那么派生类的virtual可加可不加。

1万+

被折叠的 条评论
为什么被折叠?



