面试题:基类的析构函数为何要声明为虚函数?

简介: 面试题:基类的析构函数为何要声明为虚函数?

面试题:基类的析构函数为何要声明为虚函数?

在 C++ 中,一个类的析构函数用于释放它的实例占用的资源。如果没有正确地释放这些资源,就可能会导致内存泄漏和其他严重的问题。基类的析构函数到底是否需要声明为虚函数取决于你是否会使用继承体系。

当使用继承时,如果一个基类指针指向了一个派生类对象,当对指针进行 delete 操作时,应该同时调用基类和派生类的析构函数。如果基类的析构函数不是虚拟函数,则运行时不能确认要调用哪个析构函数。如果它是虚拟的,则始终将调用最实际对象的析构函数。

考虑以下示例:

class Animal {
public:
    Animal() {
        cout << "Animal constructor" << endl;
    }
    ~Animal() {
        cout << "Animal destructor" << endl;
    }
};
class Dog : public Animal {
public:
    Dog() {
        cout << "Dog constructor" << endl;
    }
    ~Dog() {
        cout << "Dog destructor" << endl;
    }
};
int main() {
    Animal *p = new Dog();
    delete p;
    return 0;
}

在这个例子中,Dog 继承自 Animal 并实现了两个构造函数和两个析构函数。在 main() 函数中,通过基类指针创建一个 Dog 类型的对象,然后删除指针。由于 Animal 的析构函数不是虚拟的,所以仅调用了基类的析构函数:

Animal constructor
Dog constructor
Animal destructor

可以看到,Dog 类的析构函数没有被调用。如果将 Animal 的析构函数声明为虚拟,就会得到预期的结果:

class Animal {
public:
    Animal() {
        cout << "Animal constructor" << endl;
    }
    virtual ~Animal() {
        cout << "Animal destructor" << endl;
    }
};
class Dog : public Animal {
public:
    Dog() {
        cout << "Dog constructor" << endl;
    }
    ~Dog() {
        cout << "Dog destructor" << endl;
    }
};
int main() {
    Animal *p = new Dog();
    delete p;
    return 0;
}

输出结果:

Animal constructor
Dog constructor
Dog destructor
Animal destructor

在这种情况下,派生类 Dog 的析构函数正常地被调用。

总结

在使用继承时,应该将基类的析构函数声明为虚函数,这样可以确保在运行时删除派生类对象时同时调用基类和派生类的析构函数。否则运行时不能确认要调用哪个析构函数,并且可能导致内存泄漏和其他问题。需要注意的是,每个具有虚函数的对象都包含一个指向虚函数表格(vtable)的指针,从而增加了内存开销,但是这种开销相对于可靠性和程序稳定性的提升来说是值得的。

相关文章
|
编译器 C++
面试题:什么是虚函数?
面试题:什么是虚函数?
188 0
|
存储 缓存 Java
【阿里面试】C++多态和虚函数
2.1 现在假设有一个编译好的C++程序,编译没有错误,但是运行时报错,报错如下:你正在调用一个纯虚函数(Pure virtual function call error),请问导致这个错误的原因可能是什么?
446 0
【阿里面试】C++多态和虚函数
|
安全 C++
【C++面试】虚函数和纯虚函数
因为写代码时不能在一开始就确定被调用的是基类的函数,还是哪个派生类的成员函数,所以C++通过虚函数实现多态,即在基类中用virtual声明,父类可以引用子类对象(如Human* phuman1 = new Men;父类类型指针phuman指向子类对象),子类成员函数可以重写父类方法(函数)。
342 0
【C++面试】虚函数和纯虚函数
|
测试技术
软件测试面试题:请讲一讲析构函数和虚函数的用法和作用?
软件测试面试题:请讲一讲析构函数和虚函数的用法和作用?
218 0
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

热门文章

最新文章