【问题标题】:C++ load constructor from variable nameC++ 从变量名加载构造函数
【发布时间】:2020-05-12 16:29:06
【问题描述】:

我有一个学校项目,其中有一个世界模拟。老师要我做保存/加载系统,我遇到了问题。我的数据以name x y 格式保存,所以保存效果很好。

当我想加载数据时,问题就开始了。这是我的解决方案:

switch(name) {
case "Human":
    new Human(x,y);
    break;
case "Dog":
    new Dog(x,y);
    break;
}

有没有办法概括这一点?保存的name 总是与constructor name 完全相同,所以我只想做这样的事情:

string name = "Human"
new <name>(x,y)   <->   new Human(x,y);

我的解决方案工作得很好,但是按照 OOP 的规则,世界不应该知道什么样的有机体生活在上面。

【问题讨论】:

  • 您提供的代码可能没有按照您的意愿执行。您正在分配和构造一个对象,但必须引用/指向该内存(泄漏内存)。
  • 伙计们 - 我知道我的示例代码在那里没有做任何事情。这只是我想要实现的一个例子
  • 我有一个 Organizm 类,然后是从 Organism 派生的 Plants and Animals,然后是 Dog、Human、Flower 等类
  • 我只是想知道是否可以从变量构造一个对象,因此对于给定变量string name = "Human",代码将在不使用 switch 或 if 条件的情况下执行 new Human

标签: c++ class constructor


【解决方案1】:

不,目前没有。 C++ 没有反射和内省,而这正是此类工作所必需的。 (在这个方向上正在进行积极的工作,但不要指望它很快就会成为标准)。

有序列化库可以隐藏您想要的 switch 的等效项,并提供更简单、更安全的 API,它们是在生产中执行此操作的首选方式,但对于您的分配,您应该手动执行。

顺便说一句,你的代码在语法上不正确,它不应该编译,但我想我明白你的意思了。

【讨论】:

  • 我知道我的代码无法编译。它只是作为一个例子来解释这个问题。
  • @cigien 不是,根本不是。模板是一种编译时功能,您可以在运行时从文件中读取对象的类型。
  • 哦,当然,那太傻了。我没有仔细阅读这个问题。
【解决方案2】:

您可以使用宏来简化字符串比较的过程。但是您仍然需要提供需要搜索的类的列表。

#define CHECK_RETURN(name, className) if (name == #className) return new className();

std::string name = "Dog";

CHECK_RETURN(name, Human);
CHECK_RETURN(name, Dog);
CHECK_RETURN(name, Banana);

【讨论】:

  • 不,这需要反思 - 正如其他一些答案所指出的那样。但是如果你真的想要一个包含函数指针映射的基类并且每个子类负责注册它的函数指针,你可以有点假反射。如果您希望我用这种方法和示例更新答案,请告诉我。
  • 好的,我看到有人已经写了相同的答案,所以没关系。
  • 这个项目使用这种先进的方法并不复杂。我只是认为有一个相当简单的解决方案,但似乎没有。
  • 我只是想到了另一种不需要函数指针的解决方案,但需要编写一个单独的控制台应用程序来解析所有头文件并找到所有类声明并动态写入create的定义函数到源文件中。如果您在预构建事件中运行此控制台应用程序,则不必考虑它,只需在代码中的其他任何地方使用上述 create 函数的前向声明即可。但我想这比使用函数指针还要复杂。
  • @RanjeethMahankali 这通常是通过用另一种小语言 (IDL) 编写对象描述然后生成头文件类定义来完成的。这样,生成器就可以为 C++、Java、C#、Go、Rust、Python 等生成输出。
【解决方案3】:

没有。不在 C++ 中。为此,您需要反思,而这不是 C 或 C++ 可以做到的。

在某些情况下要做的是编写接口定义语言,也就是 IDL,并从中生成实现接口的代码。这些接口通常包括序列化和反序列化对象以便通过网络发送它们的能力,但它也适用于文件。

这可能比您想了解的要多。

对于一个简单的 C++ 项目,您想要实现一个工厂。我假设所有这些东西都是有机体,所以你想要一个有机体工厂:

class OrganismFactory {
public:
    static std::unique_ptr<Organism> Create(const std::string& line);
};

然后它读取一行的内容并产生一个有机体。可能使用类似你的案例陈述的东西。或者,您可以创建类名的std::mapstd::unordered_map 和指向该行其余部分的函数指针。然后每个对象类型都没有ifcase,只有映射查找和间接函数调用。您仍然需要编写代码来填充地图,并编写每个函数。

是的,根据 OOP 规则,您需要在有机体基类中为有机体在世界上所做的一切创建接口/虚拟方法。

【讨论】:

    【解决方案4】:

    您可以创建自己的创建者函数查找表来处理此问题,例如:

    class Organism
    {
    public:
        virtual ~Organism() {}
    };
    
    class Human : public Organism
    {
    ...
    };
    
    class Dog : public Organism
    {
    ...
    };
    
    ...
    
    using OrganismPtr = std::unique_ptr<Organism>;
    using CreateFunc = OrganismPtr(*)(int, int);
    std::map<std::string, CreateFunc> mymap;
    mymap["Human"] = [](int x, int y) -> OrganismPtr { return new Human(x, y); }
    mymap["Dog"] = [](int x, int y) -> OrganismPtr { return new Dog(x, y); }
    ...
    
    string name = "Human";
    OrganismPtr o = mymap[name](x, y);
    // use o as needed...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      • 2011-07-30
      相关资源
      最近更新 更多