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

C++如何在std::map中存储泛型基类的模板派生类实例

问题根因
  • 直接使用std::map<std::string, generic>值存储的方案会触发对象切片:存入容器时派生类对象会被构造为基类实例,派生类持有的特化数据、重写的虚函数表指针都会丢失,访问时只能拿到基类行为,你的判断完全正确。
  • 裸指针存储的方案存在生命周期管理缺陷:无论是栈上局部变量取地址存入容器,还是函数内局部变量取地址存入,变量离开作用域后指针就会成为悬垂指针,访问属于未定义行为。
  • 你写的智能指针版本编译失败的核心原因:add_item函数中遍历map时使用了值遍历for (auto i : m),这会尝试拷贝map中的元素,而std::unique_ptr是独占所有权的智能指针,拷贝构造/拷贝赋值函数被明确禁用,因此触发“use of deleted function”编译错误。
正确实现方案

使用std::unique_ptr<generic>作为map的值类型自动管理对象生命周期,遍历智能指针容器时使用引用遍历避免拷贝,即可实现无内存泄漏、无悬垂指针、保留完整多态行为的存储逻辑。
修正后的完整可运行代码:

#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <typeinfo>

class generic {
    std::string _name;
public:
    generic(std::string name) : _name(std::move(name)) {}
    // 多态场景下基类必须定义虚析构,否则通过基类指针销毁派生类对象会触发未定义行为
    virtual ~generic() = default;

    virtual std::string name() { return _name; }
    virtual std::string value() { return "no value in generic"; }
};

template <class T> 
class special : public generic {
    T _value;
public:
    special(std::string name, T value) : generic(std::move(name)), _value(std::move(value)) {}
    ~special() override = default;

    std::string value() override { return std::to_string(_value); }
};

template <typename T> 
void add_item(std::map<std::string, std::unique_ptr<generic>>& m, const std::string& n, const T& v) {
    m[n] = std::make_unique<special<T>>(typeid(v).name(), v);
    // 必须用引用遍历,值遍历会尝试拷贝unique_ptr触发编译错误
    for (const auto& i : m) {
        std::cout << "add_item: " << i.first << " -- " << i.second->name() << " -- " << i.second->value() << std::endl;
    }
}

int main() {
    std::map<std::string, std::unique_ptr<generic>> instances;

    add_item<int>(instances, "int", 1);
    add_item<bool>(instances, "bool", true);
    add_item<float>(instances, "float", 3.1415f);

    // 引用遍历,直接通过智能指针的->操作符调用虚函数即可,无需显式调用get()取裸指针
    for (const auto& i : instances) {
        std::cout << i.first << " -- " << i.second->name() << " -- " << i.second->value() << std::endl;
    }
    return 0;
}
补充说明
  • 如果需要在多个代码位置共享同一个实例,可以将std::unique_ptr替换为std::shared_ptr,创建对象的方法对应替换为std::make_shared即可。
  • 不要使用值类型存储多态对象,所有需要运行时多态的场景都要通过指针或引用访问对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:12:28