【问题标题】:Creating object of a class using index使用索引创建类的对象
【发布时间】:2014-07-30 12:25:00
【问题描述】:

我有这样的课程: 第一类 2 类 三级 4级 5级 六级 ... ... ... N类

在其中一个函数中,我需要根据函数的输入参数创建一个类的对象。

例如,如果输入为 1 则我需要为 Class1 创建一个对象,如果为 2 则为 Class2 对象。

是否可以不使用 if 条件创建对象?我不想要所有课程的 if 条件。

【问题讨论】:

  • 如果你有名为 Class1 ... ClassN 的类,这听起来很不对劲。这些类有多相似?你能告诉我们他们是什么样的,他们之间有什么不同吗?
  • 在我看来,您使用 C++ 而不是使用它。拥有 Class1...ClassN 是一个信号。告诉我们更多关于你试图解决的问题,也许有人会建议一个更具有 C++ 精神的解决方案。
  • 与其他人一起请求更多地了解您的设计指南 - 我们可能会提供其他内容。特别是针对您的问题,我认为您无法避免 if 条件从运行时用户输入创建不同类型
  • 您的所有班级是否共享一个共同的父班级?您打算如何管理生成的对象?
  • 我有一个基类(抽象)和一个纯虚函数。我有大约 40 个实现虚函数的派生类。虚函数将打印表格(共 40 个表格)。我将根据作为输入的表号创建派生类对象。目前我有 40 个 IF 条件来检查输入并创建派生类对象。那么有没有什么办法可以去掉所有的 IF 条件,使用单行根据输入的数字创建派生类对象呢??

标签: c++ oop


【解决方案1】:

由于您使用继承,因此您需要一个工厂函数来生成任何派生类型...

template<typename T>
base * spawn ()
{
    return new T();
}

...和一个支持随机访问的容器operator[]

如果您需要不连续的索引,请选择地图:

std::map<int, base *(*)()> map_spawner =
{
    { 0, &spawn<child_1> },
    { 1, &spawn<child_2> }
};

如果索引是连续的,则选择一个向量:

std::vector<base *(*)()> vec_spawner =
{
    &spawn<child_1>,
    &spawn<child_2>
};

工作示例:

#include <map>
#include <vector>

class base
{
    public:
        virtual ~base () = default;

    public:
        virtual int f () const = 0;
};

class child_1 : public base
{
    public:
        int f () const override { return 1; }
};

class child_2 : public base
{
    public:
        int f () const override { return 2; }
};

template<typename T>
base * spawn ()
{
    return new T();
}

int main ()
{
    // With a vector
    std::vector<base *(*)()> vec_spawner =
    {
        &spawn<child_1>,
        &spawn<child_2>
    };

    base * child = vec_spawner[0]();
    // Do something with child here ...
    delete child;

    // With a map
    std::map<int, base *(*)()> map_spawner =
    {
        { 0, &spawn<child_1> },
        { 1, &spawn<child_2> }
    };

    child = map_spawner[1]();
    // Do something with child here ...
    delete child;
}

现在您可以使用用户输入来生成特定实例。


如果您的派生类型构造函数不共享相同的参数,不幸的是据我所知您不能使用任何容器......我能想到的唯一接近的可能性是这个(工作示例):

#include <utility>

class base
{
    public:
        virtual ~base () = default;

    public:
        virtual int f () const = 0;
};

class child_1 : public base
{
    public:
        int f () const override { return 1; }
};

class child_2 : public base
{
    public:
        child_2 (int i) { (void) i; }

    public:
        int f () const override { return 2; }
};

template<typename... Args>
base * spawn (int input, Args && ... args)
{
    switch (input)
    {
        case 0: return new child_1 {};
        case 1: return new child_2 { std::forward<Args>(args)... };
        // ...
    }
    return nullptr;
}

int main ()
{
    int input = 1;
    base * child = spawn(input, 42);
    // Do something with child here ...
    delete child;
}

【讨论】:

  • 一些派生类具有参数化构造函数。如何为此定义模板?
【解决方案2】:

如果您的项目是 windows dll,我有一个可能的解决方案:

为项目中的每个派生类定义一个工厂方法。 创建Class1 对象的工厂方法应命名为us1(或者您的用户输入触发此对象创建的任何内容)。

然后使用GetProcAddress API 根据其名称(同样名称是用户输入)获取工厂方法的地址 - 获得后,只需调用它即可创建对象。

一些代码 sn-ps 来展示这个概念(很难建议一些真实的东西,因为你还没有发布你的代码)

class Base
{
public:
   void foo() = 0;
}
class Derived1 : public Base
{
public:
   void foo() {};  
}

class Derived2 : public Base
{
public:
   void foo() {};  
}


extern "C"
{
    void __declspec(dllexport) us1 (Base*& pObj)
    {
       pObj = new Derived1();
    }
    void __declspec(dllexport) us2 (Base*& pObj)
    {
       pObj = new Derived2();
    }
}

然后在你的 cpp 文件上使用类似的东西

typedef void (__cdecl FUNCPROC)(Base*&);

string userInput;
cin >> userInput;

// get a handle to your dll memory (if this is a dll)

// assuming moduleHandle points to your dll memory

FUNCPROC* pProcAddr = GetProcAddress((HMODULE)moduleHandle, userInput);

现在pProcAddr 是一个指向基于您的用户输入的受人尊敬的工厂方法的指针。

得到正确的对象

Base* pTmp = NULL;
pProcAddr(pTmp);

现在我上面的建议还没有完成,还有更多的事情需要处理(检查有效的用户输入,检查内存中存在的工厂方法等),但它应该给你一个大致的想法。

【讨论】:

    【解决方案3】:

    我在很多像你这样的情况下都使用这个代码。

    假设您的所有类Class1 ~ ClassN 都派生自一个抽象基类class Base

    #include <vector>
    #include <memory>
    #include <functional>
    
    std::vector<std::function<std::shared_ptr<Base> ()>> CtorList = {
        [] { return std::shared_ptr<Base>(new Class1); },
        [] { return std::shared_ptr<Base>(new Class2); },
        [] { return std::shared_ptr<Base>(new ClassN); },
    };
    
    // Create
    std::shared_ptr<Base> p = CtorList[num - 1]();
    

    (live example)

    【讨论】:

      【解决方案4】:

      我建议使用从输入类型映射到工厂对象的映射。工厂对象有一个返回所需类型的虚方法。因此,每个类 (Class1...ClassN) 都有一个匹配的工厂类 (FCi),它知道如何构造 (Classi) 的对象。

      typedef std::map< Input, Factory > FactoryMap;
      
      FactoryMap factoryMap;
      
      void initFactoryMap()
      {
             factoryMap[ Input1 ] = new FactoryForClass1();  // subclass of Factory, implementing constructClass
             factoryMap[ Input2 ] = new FactoryForClass2();
             factoryMap[ Input3 ] = new FactoryForClass3();
             ...
      }
      
      AbstractClass *inputHandler( Input n )
      {
          return factoryMap[n]->constructClass( n ); // may not need to pass n in
      }
      

      【讨论】:

        【解决方案5】:

        我真的要感谢你定义这么多类的努力......但我更愿意定义一个带有函数的单个类,该函数将负责创建所需的表我会尝试找出一些合乎逻辑的方法如果表格有点相似,则创建/打印表格......或者在最坏的情况下,我会使用switch 语句来选择要创建/打印的表格。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-03-13
          • 1970-01-01
          • 2010-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-04
          相关资源
          最近更新 更多