You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++如何根据用户输入为多态类型分配动态内存?关于typeid判断准确性与代码冗余的问题

问题描述

我有一段C++代码,目的是根据用户选择的索引,从存储基类和派生类指针的容器中,创建对应类型的新实例并放入新容器。代码如下:

class Base;
class DerivedA : public Base;
class DerivedB : public Base;
class DerivedC : public Base;
std::vector<Base*> vector_of_base;
vector_of_base.push_back(new Base);
vector_of_base.push_back(new DerivedA);
vector_of_base.push_back(new DerivedB);
vector_of_base.push_back(new DerivedC);
// 用户输入
std::vector<std::size_t> user_defined_index;
std::vector<Base*> new_vector;
for(auto index: user_defined_index) {
    // 不确定这样是否能按预期工作
    Base *object = nullptr;
    object = vector_of_base.at(index);
    // 判断对象类型以分配正确的实例
    if(typeid(object) == typeid(Base)) {
        // 分配Base的新实例(可选复制原实例的值)
        new_vector.push_back(new Base{object});
    } else if(typeid(object) == typeid(DerivedA)) {
        new_vector.push_back(new DerivedA{object}); // 是否可行?
    }
    // 其余派生类的判断分支...
}

现在面临两个核心问题:

  1. 派生类数量较多(约20-30个),上述if-else判断链会产生大量重复代码;
  2. 不确定当前代码的可行性:typeid(object)是否会始终返回typeid(Base),还是能正确识别指针指向的实际派生类型?比如当object指向new DerivedA创建的实例时,typeid(object)是否会返回与typeid(DerivedA)相同的结果?

补充说明:我预先创建了一个存储所有Base及派生类实例指针的vector,这些类均重载了返回std::string的GetName()方法,方法返回的字符串会展示在图形用户界面(GUI)中。用户通过GUI选择每种类型所需的数量(可为0),我需要创建一个新的vector,其中每个元素都是通过new分配的对应类型实例,实例类型由用户在GUI中选择的数量和顺序决定。


解决方案与问题解析

1. 先解决typeid的可行性问题

直接给结论:你的当前代码里typeid(object)只会返回typeid(Base*)(注意是指针类型),完全达不到识别实际对象类型的效果——因为你现在的Base类没有任何虚函数,不属于多态类型。

这里要明确两个关键点:

  • typeid作用于指针时,只有当指针指向的类是**多态类(至少有一个虚函数)**时,才会返回指针实际指向对象的类型信息;否则,它只会返回指针本身的静态类型(也就是Base*)。
  • 你代码里的比较逻辑也错了:object是Base*类型,typeid(object)得到的是指针类型的信息,而typeid(Base)是基类本身的类型,两者根本不相等。正确的写法应该是对指针解引用后再用typeid,比如typeid(*object),但前提是Base是多态类。

举个例子,把你的GetName()设为虚函数(同时加上虚析构避免内存泄漏):

class Base {
public:
    virtual std::string GetName() const { return "Base"; }
    virtual ~Base() = default;
};

此时typeid(*object)才能正确识别实际对象的类型:当object指向DerivedA实例时,typeid(*object) == typeid(DerivedA)会返回true。

不过即使能正确识别,用一堆if-else判断类型仍然是糟糕的设计,特别是你有20-30个派生类的情况——这完全违背了开闭原则(新增派生类时必须修改这段判断代码)。

2. 解决大量派生类的代码冗余问题

最经典也最优雅的解决方案是给基类添加一个虚克隆函数(Clone),让每个派生类重写这个函数,负责创建自身类型的新实例(可选复制原实例的状态)。

步骤1:给基类添加虚Clone方法

修改Base类,添加纯虚的Clone方法(既然所有派生类都重载了GetName(),把它设为纯虚更合理):

class Base {
public:
    virtual std::string GetName() const = 0;
    virtual Base* Clone() const = 0; // 纯虚克隆函数
    virtual ~Base() = default; // 必须有虚析构
};

步骤2:每个派生类实现Clone方法

以DerivedA为例,其他派生类照葫芦画瓢即可:

class DerivedA : public Base {
public:
    std::string GetName() const override { return "DerivedA"; }
    // 实现克隆:创建当前类型的新实例,可选择复制当前对象的属性
    DerivedA* Clone() const override {
        return new DerivedA(*this); // 这里假设你有合适的拷贝构造函数
    }
};

每个类只需要关心自己的克隆逻辑,不需要修改外部的判断代码。

步骤3:简化容器构建逻辑

现在你的循环代码可以简化到极致,完全不需要任何类型判断:

std::vector<Base*> new_vector;
for(auto index: user_defined_index) {
    Base* original = vector_of_base.at(index);
    // 直接调用Clone方法,自动创建对应类型的新实例
    new_vector.push_back(original->Clone());
}

不管你新增多少个派生类,这段循环代码都不需要修改,完美符合开闭原则。

额外优化:使用智能指针避免内存泄漏

如果你用C++11及以上版本,建议用std::unique_ptr代替裸指针,彻底避免手动管理内存的麻烦:

#include <memory>

// 基类的Clone方法返回unique_ptr
class Base {
public:
    virtual std::string GetName() const = 0;
    virtual std::unique_ptr<Base> Clone() const = 0;
    virtual ~Base() = default;
};

class DerivedA : public Base {
public:
    std::string GetName() const override { return "DerivedA"; }
    std::unique_ptr<Base> Clone() const override {
        // make_unique是C++14及以上可用,也可以用return std::unique_ptr<DerivedA>(new DerivedA(*this));
        return std::make_unique<DerivedA>(*this);
    }
};

// 容器也改为存储unique_ptr
std::vector<std::unique_ptr<Base>> vector_of_base;
vector_of_base.push_back(std::make_unique<DerivedA>());

// 构建新容器
std::vector<std::unique_ptr<Base>> new_vector;
for(auto index: user_defined_index) {
    auto& original = vector_of_base.at(index);
    new_vector.push_back(original->Clone());
}

内容的提问来源于stack exchange,提问作者user2138149

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:47:41