gpt4 book ai didi

c++ - 在 C++11/C++14 中的类中存储对象类型的列表/映射

转载 作者:太空狗 更新时间:2023-10-29 20:55:10 25 4
gpt4 key购买 nike

我正在为 C++ 类(注册表)编写一个模板,该类具有类似 Create 的方法和 Delete ,它实例化并存储指向对象的共享指针,但 Create方法返回对创建对象的引用,而不是共享指针本身(这里的特定范例是没有指针,甚至智能指针,在公共(public)接口(interface)中公开)。

可以处理多态类型的对象注册表,从某种意义上说,注册表专门用于基类,然后 Create是一个模板函数,可以专门用于基类的任何多态派生类。然后它返回对派生类的创建对象的引用。该类还有一个 ID 系统,因此也可以通过它引用任何对象。

我需要一个 Get类型方法 auto可以返回相同类型的对象(给定它的 ID)的对象被创建。显然这些对象被存储为指向基类的共享指针列表,所以这需要一个 dynamic_cast .

但是,我想不出一种在创建时存储原始对象类型的方法。我需要类似于 std::map<[object ID], [object type]> 的东西存储为注册表的成员变量。

我考虑过连接 std::tuple s 但添加新对象会更改其类型,因此无法将其存储为注册表的成员。我也考虑过使用 typedef 的技巧在从虚拟基类继承的新类中,因此它可以存储在指向基类的指针列表中,但随后使用 dynamic_cast要访问派生类,首先需要知道对象类型。

制作std::functions成员(member)名单调用另一个函数(在 Create 期间实例化)也将不起作用,因为返回类型不同且 auto不能在 std::function 内使用.我还尝试了各种可变参数模板的技巧。

我见过的 SO 上的所有解决方案都不合适,因为这两种方法( CreateGet )被称为同一个类,因此信息需要包含在类本身的特定实例中。

这个任务是不可能的吗?

最佳答案

这不是不可能的;但你让它变得不可能。

除了使用 Get 的模板之外,您要求的系统不需要很多技术细节。功能。让我们分解一下:

  • 您想创建一个系统,您可以在其中实例化(例如 Create )属于适当“基础”的类,然后将它们存储在关联容器中,在这种情况下,您选择了 map .
  • 您的 map 是这样定义的:
    std::map<[object ID], [object type]> m_map;

    现在,鉴于此信息。我可能会问,为什么要返回对对象的引用?更重要的是,您的 Create函数可以更容易地简化为这样的事情:
    void System::create(int id, Base *b)
    {
    m_map.emplace(id, b); // Assuming object ID is of type int
    }

    如果你这样实现了你的 create 函数,那么以下是允许的:
    class Child : public Base
    {
    public:
    Child();
    Child(const std::string &name);
    virtual ~Child();
    };

    int main()
    {
    System s;
    s.create(1, new Child("Roger"));
    }
  • 您可能对使用创建对象的手动方法不感兴趣,而是对使用更自动化的方法感兴趣。在不为我们的婴儿系统类引入新的技术措施的情况下:
    static Child *create(const std::string &name)
    {
    return new Child(name);
    }

    这允许以下用法:
    s.create(2, Child::create("William"));
  • 您希望能够基于此类检索派生类型的类。无双关语,无需创建高度特化的自动功能。您知道“在编译时”要获得的类型;而autodecltype C++14 更关注在运行时之前未知的类型。假设你知道你想要什么类型,我们的功能就简单多了:
    template<typename T>
    T Get(int id)
    {
    std::map<..>::iterator i = m_map.find(id);

    if (i != m_map.end())
    return dynamic_cast<T>(i->second);
    else return nullptr;
    }

    现在允许以下用法,继续我们的 int main() ..
    class Children : public Base
    {
    Children();
    virtual ~Children();
    void add(Child *c);
    };

    int main()
    {
    System s;

    s.create(1, Child::create("Roger"));
    s.create(2, Child::create("William"));
    s.create(3, new Children());

    s.get<Children*>(3)->add(s.get<Child*>(2)); // Add william to group

    return 0;
    }

    优点是您现在拥有的系统能够处理从“Base”派生的许多对象,而不必知道哪些对象实际上是从它派生的!这使得我们的 System类非常通用和可扩展。这也意味着任何对象创建方法都是​​“Base”类的责任;例如ChildChildren在我们的情况下。对于后者,我们没有实现对象工厂方法,因为此时它不实用。
  • 您想从注册表中删除一个对象,因此:
    void System::delete(int id)
    {
    m_map.erase(id);
    }

  • 现在我们有一个非常实用的注册系统,可以为任何类提供服务。重要的是不要滥用这些注册表来服务“太”泛型类型。最好对哪些类家族保证他们自己的注册表进行分层。

    需要考虑的事项:

    当您向 map 添加对象时,它们会自动转换为 Base 类型,但由于多态性,指针实际上指向内存中的不同位置,具有自己的一组值和功能。这就是为什么可以将一种类型动态转换为另一种类型以便您可以取回派生类型的原因。事实上,引用对象要好得多 外面系统通过 id(句柄)而不是对他们规定的内容的引用。

    请注意,我在这个例子中使用了原始指针。如果你想使用智能指针,一定要考虑到映射已经为你处理了内存。如果没有,则在使用 Get 时将无法使用内存。功能。这是一个风格问题,但也是一个极具争议的问题。有效的反对意见。

    另外,非常重要:

    考虑使用 std::unordered_map如果您的系统涉及通过 Get 获取对象功能。原因很简单:对象是无序的。这使得遍历 unordered_map 以检索包含的对象更容易。而在ordered_map std::map Get函数必须遍历所有对象,直到找到它需要的对象。为此: 使用 std::unordered_map当您知道要检索值/对象时;并使用 std::map当你知道你只会迭代它们时。

    关于c++ - 在 C++11/C++14 中的类中存储对象类型的列表/映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36534133/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com