题目
实现一个简单的图形绘制系统
信息
- 类型:问答
- 难度:⭐⭐
考点
继承与派生,虚函数与多态,纯虚函数与抽象类,动态内存管理
快速回答
实现要点:
- 定义抽象基类
Shape,包含纯虚函数draw()和area() - 派生
Circle和Rectangle类并重写虚函数 - 使用基类指针容器管理不同图形对象
- 正确实现深拷贝防止内存泄漏
- 通过虚析构函数确保正确释放资源
问题描述
设计一个图形系统,支持圆形和矩形两种图形。要求:
- 定义抽象基类
Shape,包含纯虚函数virtual void draw() const = 0和virtual double area() const = 0 - 实现派生类
Circle(半径)和Rectangle(长宽) - 创建
drawAllShapes()函数,接收vector<Shape*>并调用各元素的draw()和area() - 实现深拷贝功能
代码实现
#include <iostream>
#include <vector>
#include <cmath>
// 抽象基类
class Shape {
public:
virtual void draw() const = 0;
virtual double area() const = 0;
virtual Shape* clone() const = 0; // 克隆接口
virtual ~Shape() {} // 虚析构函数
};
// 圆形派生类
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
void draw() const override {
std::cout << "Drawing Circle with radius: " << radius
<< ", Area: " << area() << std::endl;
}
double area() const override {
return 3.14159 * radius * radius;
}
Shape* clone() const override {
return new Circle(*this); // 深拷贝
}
};
// 矩形派生类
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
void draw() const override {
std::cout << "Drawing Rectangle " << width << "x" << height
<< ", Area: " << area() << std::endl;
}
double area() const override {
return width * height;
}
Shape* clone() const override {
return new Rectangle(*this); // 深拷贝
}
};
// 绘制所有图形
void drawAllShapes(const std::vector<Shape*>& shapes) {
for (const auto& shape : shapes) {
shape->draw(); // 多态调用
}
}
// 深拷贝函数
std::vector<Shape*> cloneShapes(const std::vector<Shape*>& original) {
std::vector<Shape*> clones;
for (const auto& shape : original) {
clones.push_back(shape->clone()); // 多态克隆
}
return clones;
}
// 释放内存
void deleteShapes(std::vector<Shape*>& shapes) {
for (auto& shape : shapes) {
delete shape; // 多态删除
}
shapes.clear();
}核心原理
- 多态机制:通过基类指针调用虚函数时,根据实际对象类型决定调用哪个版本的函数
- 虚函数表:编译器为每个含虚函数的类创建vtable,存储函数指针
- 抽象类:包含纯虚函数(
=0)的类不能实例化,用于定义接口规范
最佳实践
- 基类析构函数必须声明为
virtual,确保通过基类指针删除派生类对象时正确调用派生类析构函数 - 使用
override关键字明确表示重写虚函数,避免意外隐藏 - 遵循Rule of Three:当需要自定义拷贝构造/赋值操作时,通常也需要自定义析构函数
常见错误
- 内存泄漏:忘记释放
new分配的对象 - 对象切片:通过值传递派生类对象给基类参数时丢失派生类信息
- 缺少虚析构函数:导致派生类资源泄漏
- 浅拷贝问题:默认拷贝构造函数仅复制指针,导致重复释放
扩展知识
- C++11智能指针:使用
unique_ptr/shared_ptr自动管理内存 - 工厂模式:通过工厂方法创建对象,隐藏具体类实现
- typeid与dynamic_cast:运行时类型识别(RTTI)机制,但应优先使用多态