如何修复C++混合Vehicle子类数组中的对象切割问题?
解决C++对象切割与多态打印问题
你的判断完全正确:问题核心就是对象切割。当前Road类中vehicles是Vehicle类型的数组,存储的是实际的基类对象,而非指向子类对象的指针/引用。当你将Car或Lorry对象赋值给数组中的Vehicle元素时,子类特有的成员(比如Car的currentLane)会被切割丢弃,同时虚函数表也会被替换为基类的,导致无法触发子类的print方法。
下面是具体的解决方案:
1. 给基类添加虚析构与克隆方法
多态场景下必须给基类添加虚析构函数,否则销毁子类指针时无法调用子类析构;同时需要添加克隆方法,用于创建子类对象的副本,避免直接赋值导致切割。
修改Vehicle类:
class Vehicle { int speed; int entryTime; int currentPositionInLane; int minimumFollowingDistance; public: // 虚析构函数,确保子类析构被正确调用 virtual ~Vehicle() = default; virtual void print() const { std::cout << "speed: " << speed << std::endl; std::cout << "entryTime: " << entryTime << std::endl; std::cout << "currentPositionInLane: " << currentPositionInLane << std::endl; std::cout << "minimumFollowingDistance: " << minimumFollowingDistance << std::endl; } // 纯虚克隆方法,子类必须实现 virtual Vehicle* clone() const = 0; };
2. 子类实现克隆方法
让Car和Lorry分别实现clone方法,返回自身对象的副本:
Car类修改
class Car : public Vehicle { int currentLane; public: Car(int, int, int, int, int); void pass(const Vehicle&); void print() const override { Vehicle::print(); std::cout << "currentLane: " << currentLane << std::endl; } // 实现克隆方法 Vehicle* clone() const override { return new Car(*this); } };
Lorry类修改
class Lorry: public Vehicle { public: Lorry(int speed, int entryTime, int currentPositionInLane, int minimumFollowingDistance) : Vehicle(speed, entryTime, currentPositionInLane, minimumFollowingDistance){} // 实现克隆方法 Vehicle* clone() const override { return new Lorry(*this); } };
3. 修改Road类的成员与方法
将Road中存储车辆的数组改为指针数组(或用智能指针更安全),存储指向子类对象的指针,保留多态性。
修改Road类定义
class Road { double updateFrequency; double currentTime; Vehicle** vehicles; // 改为指针数组,存储指向Vehicle子类的指针 int vehicleCount; public: Road(); ~Road(); // 添加析构函数释放内存 void update(); bool checkIfFinished(); void checkForUserInterrupt(); void saveToFile(); bool setCurrentTime(double); bool setUpdateFrequency(double); void addVehicle(const Vehicle& vehicle); void print(); };
实现Road的构造、析构与addVehicle方法
// 构造函数初始化成员 Road::Road() : updateFrequency(0), currentTime(0), vehicles(nullptr), vehicleCount(0) {} // 析构函数释放所有车辆对象与指针数组 Road::~Road() { for (int i = 0; i < vehicleCount; i++) { delete vehicles[i]; // 调用子类析构 } delete[] vehicles; // 释放指针数组 } // 修改addVehicle,通过克隆创建子类副本并存储指针 void Road::addVehicle(const Vehicle& vehicle) { Vehicle** temp = new Vehicle*[vehicleCount + 1]; // 复制原有指针 for (int i = 0; i < vehicleCount; i++) { temp[i] = vehicles[i]; } // 克隆传入的车辆对象,存储子类指针 temp[vehicleCount] = vehicle.clone(); vehicleCount++; // 替换旧数组 delete[] vehicles; vehicles = temp; }
修改Road的print方法
通过指针调用虚函数,触发动态绑定:
void Road::print() { std::cout << "updateFrequency: " << updateFrequency << std::endl; std::cout << "currentTime: " << currentTime << std::endl; std::cout << std::endl << "The vehicles on the road: " << std::endl << std::endl; for (int i = 0; i < vehicleCount; i++) { vehicles[i]->print(); // 指针调用虚函数,自动匹配子类实现 std::cout << std::endl; } }
更安全的替代方案:使用智能指针
如果不想手动管理内存,可以用std::vector<std::unique_ptr<Vehicle>>替代原始指针数组,避免内存泄漏:
修改Road类成员:
#include <vector> #include <memory> class Road { double updateFrequency; double currentTime; std::vector<std::unique_ptr<Vehicle>> vehicles; // 用智能指针容器 public: // ... 其他方法不变 void addVehicle(const Vehicle& vehicle) { // 克隆对象并放入智能指针容器 vehicles.push_back(std::unique_ptr<Vehicle>(vehicle.clone())); } void print() { std::cout << "updateFrequency: " << updateFrequency << std::endl; std::cout << "currentTime: " << currentTime << std::endl; std::cout << "\nThe vehicles on the road: \n\n"; for (const auto& veh : vehicles) { veh->print(); std::cout << "\n"; } } };
这种方式不需要手动写析构函数,智能指针会自动管理对象生命周期。
内容的提问来源于stack exchange,提问作者jdsflk
相关产品推荐
相关产品推荐

