面试题答案
一键面试- 内存释放过程
- 当使用
delete []
释放通过new []
创建的派生类对象数组时,首先会从数组的最后一个元素开始,依次调用每个派生类对象的析构函数。由于基类有虚析构函数,这里会调用派生类的析构函数(如果派生类有自己的析构函数重写),然后再调用基类的析构函数。 - 完成所有对象析构函数的调用后,系统会释放分配给这个数组的内存空间。
- 当使用
- 可能遇到的问题
- 如果基类析构函数不是虚函数,在使用
delete []
释放派生类对象数组时,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类中申请的资源(如动态分配的内存等)无法释放,从而造成内存泄漏。
- 如果基类析构函数不是虚函数,在使用
- 代码示例
#include <iostream>
#include <string>
// 基类
class Base {
public:
// 基类非虚析构函数(错误示例,应改为虚析构函数)
// ~Base() {}
virtual ~Base() {
std::cout << "Base destructor called" << std::endl;
}
};
// 派生类
class Derived : public Base {
private:
std::string data;
public:
Derived(const std::string& s) : data(s) {
std::cout << "Derived constructor called" << std::endl;
}
~Derived() {
std::cout << "Derived destructor called for " << data << std::endl;
}
};
int main() {
Base* arr[] = {new Derived("obj1"), new Derived("obj2")};
for (size_t i = 0; i < 2; ++i) {
delete arr[i];
}
// 或者如果是new []创建的数组
// Base* arr = new Derived[2]{"obj1", "obj2"};
// delete [] arr;
return 0;
}
- 解决方案
- 确保基类的析构函数声明为虚函数,如上述代码中修改后的
Base
类的析构函数。这样在通过delete []
释放派生类对象数组时,会正确地调用每个派生类对象的析构函数,进而正确释放所有资源。
- 确保基类的析构函数声明为虚函数,如上述代码中修改后的
如果是new []
创建数组并释放,示例代码如下:
#include <iostream>
#include <string>
class Base {
public:
virtual ~Base() {
std::cout << "Base destructor called" << std::endl;
}
};
class Derived : public Base {
private:
std::string data;
public:
Derived(const std::string& s) : data(s) {
std::cout << "Derived constructor called" << std::endl;
}
~Derived() {
std::cout << "Derived destructor called for " << data << std::endl;
}
};
int main() {
Base* arr = new Derived[2]{"obj1", "obj2"};
delete [] arr;
return 0;
}