面试题答案
一键面试特殊使用场景
- 工厂模式中对象创建与销毁:在工厂模式下,通过工厂类创建对象,返回的指针类型通常是基类指针。例如,假设有一个
Shape
基类和Circle
、Rectangle
等派生类。工厂函数可能这样写:
class Shape {
public:
virtual void draw() = 0;
// 这里假设还没有虚析构函数
};
class Circle : public Shape {
public:
void draw() override {
// 绘制圆形的代码
}
};
class ShapeFactory {
public:
static Shape* createShape(const std::string& type) {
if (type == "circle") {
return new Circle();
}
return nullptr;
}
};
当使用工厂创建对象并释放时:
Shape* shape = ShapeFactory::createShape("circle");
shape->draw();
delete shape; // 如果Shape没有虚析构函数,这里只会调用Shape的析构函数,Circle的析构函数不会被调用,导致内存泄漏
- 策略模式中不同策略对象的销毁:在策略模式中,不同的策略类继承自一个策略基类。例如,有一个
SortStrategy
基类和QuickSortStrategy
、MergeSortStrategy
等派生策略类。
class SortStrategy {
public:
virtual void sort(int* arr, int size) = 0;
// 假设还没有虚析构函数
};
class QuickSortStrategy : public SortStrategy {
public:
void sort(int* arr, int size) override {
// 快速排序代码
}
};
class Context {
private:
SortStrategy* strategy;
public:
Context(SortStrategy* s) : strategy(s) {}
~Context() {
delete strategy; // 如果SortStrategy没有虚析构函数,同样会导致派生类析构函数不被调用
}
void executeSort(int* arr, int size) {
strategy->sort(arr, size);
}
};
面临的挑战
- 内存泄漏:如上述例子,如果基类没有虚析构函数,当通过基类指针释放派生类对象时,派生类的析构函数不会被调用,从而导致派生类中分配的资源无法释放,产生内存泄漏。
- 未定义行为:可能导致程序在运行时出现未定义行为,尤其是在析构函数中涉及到资源清理(如文件关闭、内存释放等)操作时,如果没有正确调用派生类析构函数,可能会访问已释放的内存或其他未定义的状态。
保证正确实现的方法
- 在基类中定义虚析构函数:在上述
Shape
基类中添加虚析构函数:
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
在SortStrategy
基类中同样添加:
class SortStrategy {
public:
virtual void sort(int* arr, int size) = 0;
virtual ~SortStrategy() = default;
};
- 使用智能指针:结合智能指针可以更好地管理对象的生命周期,进一步避免内存泄漏。例如,在工厂模式中可以返回
std::unique_ptr<Shape>
:
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() override {
// 绘制圆形的代码
}
};
class ShapeFactory {
public:
static std::unique_ptr<Shape> createShape(const std::string& type) {
if (type == "circle") {
return std::make_unique<Circle>();
}
return nullptr;
}
};
在使用时:
auto shape = ShapeFactory::createShape("circle");
if (shape) {
shape->draw();
}
// 智能指针离开作用域时会自动调用析构函数,无需手动delete
在策略模式的Context
类中也可以使用智能指针管理策略对象:
class Context {
private:
std::unique_ptr<SortStrategy> strategy;
public:
Context(std::unique_ptr<SortStrategy> s) : strategy(std::move(s)) {}
void executeSort(int* arr, int size) {
strategy->sort(arr, size);
}
};
这样可以确保在对象生命周期结束时,无论是基类还是派生类的析构函数都会被正确调用,有效避免内存泄漏和未定义行为。