如题,当一个类为基类的时候,通常其析构函数被声明为虚函数,这是为啥?
class BaseCls{
public:
BaseCls() { printf("BaseCls()n"); }
~BaseCls() { printf("~BaseCls()n"); }
void test_func() { printf("Base::test_func()n"); }
};
class SubCls : public BaseCls
{
public:
SubCls() { printf("SubCls()n"); }
~SubCls() { printf("~SubCls()n"); }
void test_func() { printf("SubCls::test_func()n");
};
int main(void)
{
BaseCls *base = new SubCls;
base->test_func();
delete base;
return 0;
}
编译运行:
在main()函数中,定义了BaseCls类型指针,根据赋值兼容性原则,该指针可以指向动态生成子类SubCls对象的地址,此时动态生成的SubCls对象已经被充当基类使用,因为BaseCls中的test_func()函数是普通函数(并非虚函数)不能发生多态,所以打印的是基类的”Base::test_func()n”。
代码”delete base;”我们希望用来释放SubCls的空间,即调用SubCls的析构函数,释放完毕后BaseCls的析构函数。然而编译器只是根据指针类型是BaseCls而只是调用BaseCls的析构函数,SubCls的析构函数得不到调用,若在SubCls构造函数中动态分配的空间,在析构函数释放空间,那么这样就造成内存泄漏了。
如何改进?让基类BaseCls的析构函数声明为虚函数,使其在delete base时发生多态即可:
class BaseCls{
public:
//...
virtual ~BaseCls()
{
printf("~BaseCls()n");
}
//...
};
编译运行:
将构造函数中声明为virtual后,编译器就不会简单的只根据base指针的类型而决定调用对象的构造函数,而是依据指针base所指向的实际对象而调用其构造函数,这不就是多态吗?
最后,再补充两个问题?
(1) 析构函数可以被声明为虚函数,那构造函数可以声明为虚函数吗?
- 答案是不可以。因为虚函数的调用依靠于虚函数表。然而在构造函数执行完毕后,虚函数表指针才被正确初始化。
(2) 构造函数不可以被声明为虚函数,那么构造函数的实现体中,可以实现多态吗?
- 答案还是不可以。因为析构函数一旦被调用,虚函数表指针就会销毁了。那么同理,在构造函数中实现体中也不可能发生多态行为,因为在构造函数执行时,虚函数表指针还没被正确初始化。