C++中基类指针向量内派生对象的区分、访问与拆分问题
嘿,我来帮你搞定这个C++多态的问题!你现在面临的是如何在存储基类指针的vector中访问派生类专属成员,以及如何把这个大vector拆分成对应派生类型的子vector,咱们一步步来解决。
首先得先指出你代码里的一个小问题:Banana和Apple里的cout_banana、cout_apple都是private成员,外部根本调用不了,得先把它们改成public才行,不然后面的操作都白搭。
接下来分两种方案给你讲解:
方案一:用
dynamic_cast识别类型(直接解决你的两个需求) dynamic_cast是C++里用来在继承体系中做类型转换的工具,它能帮我们判断一个基类指针实际指向的是哪个派生类对象。结合unique_ptr的移动语义,我们就能完成类型判断、调用专属函数和拆分vector的操作。
不过要注意:你原来的FruitBox构造函数参数是const vector<unique_ptr<Fruit>> &,但unique_ptr是不可拷贝的,要转移所有权必须用移动,所以得把构造函数改成接受右值引用或者传值,这样才能拿到所有权。
修改后的代码如下:
#include <vector> #include <memory> #include <iostream> using namespace std; class Fruit {}; class Banana: public Fruit { public: // 改成public权限 void cout_banana() { cout << "i am a banana" << endl; } }; class Apple : public Fruit { public: // 改成public权限 void cout_apple() { cout << "i am an apple" << endl; } }; class FruitBox { vector<unique_ptr<Banana>> vec_banana; vector<unique_ptr<Apple>> vec_apple; public: // 改成接受右值引用,支持unique_ptr的所有权转移 FruitBox(vector<unique_ptr<Fruit>> &&fruits) { for (auto &f : fruits) { // 尝试将基类指针转换为Banana指针 if (Banana* banana_ptr = dynamic_cast<Banana*>(f.get())) { // 调用Banana专属成员函数 banana_ptr->cout_banana(); // 移动unique_ptr到对应子vector vec_banana.push_back(move(f)); } // 尝试转换为Apple指针 else if (Apple* apple_ptr = dynamic_cast<Apple*>(f.get())) { // 调用Apple专属成员函数 apple_ptr->cout_apple(); // 移动unique_ptr到对应子vector vec_apple.push_back(move(f)); } } } }; int main() { // 注意main的标准返回类型是int,不是void vector<unique_ptr<Fruit>> inputs; inputs.emplace_back(make_unique<Banana>()); // 推荐用make_unique替代直接new inputs.emplace_back(make_unique<Apple>()); FruitBox fbox = FruitBox(move(inputs)); // 移动inputs的所有权给FruitBox return 0; }
方案二:用多态(虚函数)实现更优雅的打印(推荐)
如果你的需求只是调用派生类的专属打印函数,其实用多态更符合面向对象的设计思想,不需要手动判断类型,代码更简洁也更易扩展。
我们可以在基类Fruit里定义一个纯虚函数print(),然后每个派生类重写这个函数,这样直接调用基类指针的print()就会自动触发对应派生类的实现:
#include <vector> #include <memory> #include <iostream> using namespace std; class Fruit { public: // 定义纯虚打印函数,强制派生类重写 virtual void print() const = 0; // 基类必须加虚析构函数,避免通过基类指针删除派生类对象时的内存泄漏 virtual ~Fruit() = default; }; class Banana: public Fruit { public: void print() const override { // 重写虚函数 cout << "i am a banana" << endl; } }; class Apple : public Fruit { public: void print() const override { // 重写虚函数 cout << "i am an apple" << endl; } }; class FruitBox { vector<unique_ptr<Banana>> vec_banana; vector<unique_ptr<Apple>> vec_apple; public: FruitBox(vector<unique_ptr<Fruit>> &&fruits) { for (auto &f : fruits) { // 直接调用多态print函数,无需判断类型 f->print(); // 拆分vector仍需要dynamic_cast识别类型 if (Banana* banana_ptr = dynamic_cast<Banana*>(f.get())) { vec_banana.push_back(move(f)); } else if (Apple* apple_ptr = dynamic_cast<Apple*>(f.get())) { vec_apple.push_back(move(f)); } } } }; int main() { vector<unique_ptr<Fruit>> inputs; inputs.emplace_back(make_unique<Banana>()); inputs.emplace_back(make_unique<Apple>()); FruitBox fbox = FruitBox(move(inputs)); return 0; }
一些关键注意事项
- 基类必须加虚析构函数:如果基类没有虚析构,通过基类指针删除派生类对象会导致未定义行为和内存泄漏。
dynamic_cast的性能:它会在运行时做类型检查,有一定性能开销;如果对性能要求极高,可以给基类加类型标识枚举,但大多数场景下dynamic_cast足够用。unique_ptr的所有权:unique_ptr表示独占所有权,必须用std::move转移所有权,不能直接拷贝。- 优先用
make_unique:比起直接new,make_unique更安全,能避免异常场景下的内存泄漏。
内容的提问来源于stack exchange,提问作者Phil-ZXX
相关产品推荐
相关产品推荐

