如何在接收基类对象的函数中创建无切片的std::vector<派生类>
多态集合解决方案:用智能指针容器+Clone模式实现无切片的派生类集合
先梳理下你的现有代码和问题背景:
初始类定义
class Animal{ public: virtual void makeSound(){ std::cout << "I dont know what sound to make." << std::endl; } }; class Dog: public Animal{ public: void makeSound() override{ std::cout << "Woof!" << std::endl; } }; class Cat: public Animal{ public: void makeSound() override{ std::cout << "Meow!" << std::endl; } };
单个对象的无切片拷贝方案
你已经通过添加clone虚函数和虚析构解决了单个多态对象的拷贝问题,这里修正一个小细节:unique_ptr需要用->访问成员函数:
class Animal { public: virtual void makeSound(){ std::cout << "I dont know what sound to make." << std::endl; } virtual ~Animal() = default; // 必须添加,确保派生类析构被正确调用 virtual std::unique_ptr<Animal> clone() const = 0; // 纯虚克隆函数 }; class Dog : public Animal { public: void makeSound() override{ std::cout << "Woof!" << std::endl; } std::unique_ptr<Animal> clone() const override { return std::make_unique<Dog>(*this); } }; class Cat : public Animal { public: void makeSound() override{ std::cout << "Meow!" << std::endl; } std::unique_ptr<Animal> clone() const override { return std::make_unique<Cat>(*this); } }; void MakeSound(Animal& animal){ auto another_animal = animal.clone(); another_animal->makeSound(); // 这里要改用->,因为unique_ptr是智能指针 }
针对集合需求的解决方案
要创建运行时指定大小的派生类拷贝集合,同时支持自定义派生类、兼容vector的所有标准操作,最直接的方案是使用std::vector<std::unique_ptr<Animal>>作为容器,结合已有的clone方法实现:
核心实现思路
- 用
vector<unique_ptr<Animal>>替代vector<Animal>:智能指针存储基类指针,避免对象切片 - 依赖
clone的多态性:不管是内置的Dog/Cat还是用户自定义的Godzilla,只要实现了clone就能被正确拷贝 - 完全兼容vector的标准方法:
push_back、empty、[]、reserve等操作和普通vector用法一致
示例代码
1. 从原型对象生成指定大小的拷贝集合
// 根据原型对象创建count个拷贝的集合 std::vector<std::unique_ptr<Animal>> createPolymorphicCopies(const Animal& prototype, size_t count) { std::vector<std::unique_ptr<Animal>> collection; collection.reserve(count); // 预分配内存,提升批量插入的性能 for (size_t i = 0; i < count; ++i) { collection.push_back(prototype.clone()); } return collection; } // 测试用例 int main() { // 创建5个Dog的拷贝集合 Dog my_dog; auto dog_collection = createPolymorphicCopies(my_dog, 5); // 遍历集合调用方法 for (const auto& animal : dog_collection) { animal->makeSound(); // 输出5次Woof! } // 使用vector的标准操作 if (!dog_collection.empty()) { dog_collection[2]->makeSound(); // 访问第三个元素 } // 追加自定义派生类对象(比如Godzilla) class Godzilla : public Animal { public: void makeSound() override { std::cout << "Roar!!!" << std::endl; } std::unique_ptr<Animal> clone() const override { return std::make_unique<Godzilla>(*this); } }; Godzilla zilla; dog_collection.push_back(zilla.clone()); dog_collection.back()->makeSound(); // 输出Roar!!! return 0; }
2. 拷贝现有多态集合
如果你需要从一个已有的多态集合生成拷贝,同样可以用clone实现:
std::vector<std::unique_ptr<Animal>> copyPolymorphicCollection(const std::vector<std::unique_ptr<Animal>>& source) { std::vector<std::unique_ptr<Animal>> dest; dest.reserve(source.size()); for (const auto& animal_ptr : source) { dest.push_back(animal_ptr->clone()); } return dest; }
方案优势
- 无对象切片:所有派生类对象都以完整形式存储在容器中
- 扩展性强:用户新增派生类时,只要实现
clone方法就能直接加入集合,无需修改容器相关代码 - 内存安全:
unique_ptr自动管理内存,无需手动释放,避免内存泄漏 - 完全兼容vector操作:所有vector的标准方法都能正常使用,学习成本低
内容的提问来源于stack exchange,提问作者张亦弛
相关产品推荐
相关产品推荐

