面试题答案
一键面试-
-
为什么
GrandParent
的析构函数需要是虚拟的: 在C++中,当通过基类指针删除派生类对象时,如果基类的析构函数不是虚拟的,那么只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类中分配的资源(如动态分配的内存等)无法正确释放,从而产生内存泄漏。在上述代码中,如果GrandParent
的析构函数不是虚拟的,当delete
一个指向Child
对象的GrandParent*
指针时,只会调用GrandParent
的析构函数,Parent
和Child
的析构函数不会被调用,Child
对象中动态分配的data
内存就无法释放。 -
如果
GrandParent
的析构函数不是虚拟的,在delete
指向Child
对象的GrandParent*
指针时的情况: 只会调用GrandParent
的析构函数,输出“GrandParent destructor”。Parent
和Child
的析构函数不会被调用,Parent
类中data
所指向的内存没有被释放,造成内存泄漏。
-
-
-
忘记在
Parent
的析构函数中写delete data;
导致的内存管理问题: 会导致内存泄漏。因为data
是在Parent
类的构造函数中通过new
分配的内存,如果在析构函数中没有delete data;
,那么当Parent
对象或其派生类Child
对象被销毁时,data
所指向的内存不会被释放。 -
利用智能指针避免这类问题: 可以使用
std::unique_ptr
来管理动态分配的内存。std::unique_ptr
在对象销毁时会自动调用其管理对象的析构函数,从而确保内存正确释放。修改后的代码如下:
-
#include <memory>
#include <iostream>
class GrandParent {
public:
GrandParent() { std::cout << "GrandParent constructor" << std::endl; }
virtual ~GrandParent() { std::cout << "GrandParent destructor" << std::endl; }
void someFunction() { std::cout << "GrandParent someFunction" << std::endl; }
};
class Parent : public GrandParent {
public:
Parent() { std::cout << "Parent constructor" << std::endl; }
~Parent() override { std::cout << "Parent destructor" << std::endl; }
void someFunction() override { std::cout << "Parent someFunction" << std::endl; }
std::unique_ptr<int> data;
Parent() : data(std::make_unique<int>(0)) {}
};
class Child : public Parent {
public:
Child() { std::cout << "Child constructor" << std::endl; }
~Child() override { std::cout << "Child destructor" << std::endl; }
void someFunction() override { std::cout << "Child someFunction" << std::endl; }
};
在上述代码中,std::unique_ptr<int>
代替了int*
,std::make_unique<int>(0)
用于创建int
对象并由std::unique_ptr
管理。这样,在Parent
和Child
对象销毁时,std::unique_ptr
会自动释放其所管理的内存,避免了内存泄漏。