面试题答案
一键面试设计思路
- 内存管理:使用智能指针来管理
Shape
对象,以避免手动内存管理带来的内存泄漏风险。std::unique_ptr
适合独占所有权的场景,而std::shared_ptr
适用于共享所有权的情况。这里由于ShapeContainer
将管理Shape
对象的生命周期,使用std::unique_ptr
即可。 - 代码复用方式:选择组合方式,在
ShapeContainer
类中包含一个容器(如std::vector
)来存储Shape
对象的智能指针。这种方式相比于继承更加灵活,因为继承是一种“是一种”的关系,而ShapeContainer
并不是一种Shape
,而是“包含”多种Shape
,组合更符合这种关系。 - 功能实现:
- 添加
Shape
对象:在ShapeContainer
类中提供一个函数,接受std::unique_ptr<Shape>
参数,并将其添加到容器中。 - 删除特定
Shape
对象:提供一个函数,通过某种标识(如索引或特定条件)找到要删除的Shape
对象,并从容器中移除。 - 遍历绘制所有
Shape
对象:遍历容器,调用每个Shape
对象的draw
函数。
- 添加
核心代码
#include <iostream>
#include <memory>
#include <vector>
// 基类Shape
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
// 派生类Circle
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a Circle" << std::endl;
}
};
// 派生类Rectangle
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a Rectangle" << std::endl;
}
};
// ShapeContainer类
class ShapeContainer {
private:
std::vector<std::unique_ptr<Shape>> shapes;
public:
// 添加Shape对象
void addShape(std::unique_ptr<Shape> shape) {
shapes.emplace_back(std::move(shape));
}
// 删除特定Shape对象(通过索引)
void removeShape(size_t index) {
if (index < shapes.size()) {
shapes.erase(shapes.begin() + index);
}
}
// 遍历绘制所有Shape对象
void drawAll() const {
for (const auto& shape : shapes) {
shape->draw();
}
}
};
避免内存泄漏
- 智能指针的使用:使用
std::unique_ptr
来管理Shape
对象,std::unique_ptr
在其析构时会自动释放所指向的对象。当ShapeContainer
对象被销毁时,其包含的std::vector
会销毁其中所有的std::unique_ptr
,进而释放对应的Shape
对象,避免了手动释放内存带来的遗漏和错误。 - 正确的资源管理流程:在添加
Shape
对象时,使用std::move
将std::unique_ptr
的所有权转移到ShapeContainer
的std::vector
中,确保资源的正确转移和唯一所有权。在删除Shape
对象时,std::vector
的erase
操作会自动调用std::unique_ptr
的析构函数,释放相应的Shape
对象。
不同代码复用方式对整体架构的影响
- 继承:如果使用继承,
ShapeContainer
将从Shape
继承,这意味着ShapeContainer
也是一种Shape
,这与实际需求不符。此外,继承会导致ShapeContainer
拥有Shape
的所有接口和状态,增加了不必要的复杂性,并且在管理多种Shape
对象时不够灵活。例如,如果需要添加新的Shape
类型,继承方式需要在ShapeContainer
类中添加更多的处理逻辑,违反了开闭原则。 - 组合:通过组合方式,
ShapeContainer
只需要关心如何管理Shape
对象,而不需要继承Shape
的接口和状态。这种方式更加灵活,易于扩展。当有新的Shape
类型添加时,只需要在外部创建该Shape
类型的std::unique_ptr
并添加到ShapeContainer
中,ShapeContainer
本身不需要修改,符合开闭原则。同时,组合方式使得ShapeContainer
和Shape
之间的耦合度降低,提高了代码的可维护性和可复用性。