基类指针或引用可以指向或是引用派生类对象,下文以指针为例作讲解。引用的例子类似,不再赘述。
如果基类指针向派生类对象,则删除此指针时,我们希望调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。
若使用基类指针操作派生类,需要防止在析构时,只析构基类,而不析构派生类。
但是,如果析构函数不被声明成虚函数,则编译器采用的绑定方式是静态绑定,在删除基类指针时,只会调用基类析构函数,而不调用派生类析构函数,这样就会导致基类指针指向的派生类对象析构不完全。若是将析构函数声明为虚函数,则可以解决此问题。
比较以下三个例子:
1、第一段代码
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC = new string[10]; cout << "BC::BC()!" << endl;};
~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
DC *p = new DC();
p->fun1();
delete p;
return 0;
}
运行结果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!
这段代码中,基类析构函数不是虚函数。在main函数中,用子类指针操作子类。释放指针P的过程是:先释放继承类的资源,再释放基类资源,则释放是完全的,不存在问题。
2、第二段代码
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
BC *p = new DC;
p->fun1();
delete p;
return 0;
}
输出结果:
BC::BC()!
DC::DC()!
BC::fun1()!
free strsBC in BC::~BC()!
这段代码中,基类析构函数不是虚函数,但在main函数中,用基类指针操作子类,释放指针P的过程是:只是释放了基类申请的资源,而没有调用子类的析构函数,没有释放了子类申请的资源。调用fun1()函数,执行的也是基类定义的函数。
一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。
要想解决上述问题,需要声明析构函数是虚函数。
3、第三段代码:
#include<iostream>
using namespace std;
class BC{
public:
BC () { strsBC =new string[10]; cout << "BC::BC()!" << endl;};
virtual ~BC() {delete [] strsBC; cout << "free strsBC in BC::~BC()!" << endl;};
virtual void fun1() { cout << "BC::fun1()!" << endl; };
private:
string * strsBC;
};
class DC : public BC{
public:
DC () {pa = new int[6]; cout << "DC::DC()!" << endl; };
~DC () {delete [] pa; cout << "free pa in DC::~DC()!" << endl; };
void fun1() { cout << "DC::fun1()!" << endl; };
private:
int *pa;
};
int main(){
BC *p = new DC;
p->fun1();
delete p;
return 0;
}
运行结果:
BC::BC()!
DC::DC()!
DC::fun1()!
free pa in DC::~DC()!
free strsBC in BC::~BC()!
这段代码中,基类析构函数是虚函数。在main函数中,用基类指针操作子类,释放指针P的过程是:先释放子类申请的资源,再调用基类析构函数,释放父类申请的资源。调用fun1()函数,执行的也是子类中定的函数,表现出了多态特征。