【问题标题】:Class lookup structure array in C++C++中的类查找结构数组
【发布时间】:2011-02-19 20:01:45
【问题描述】:

我正在尝试创建一个将输入字符串链接到类的结构数组,如下所示:

struct {string command; CommandPath cPath;} cPathLookup[] = {
    {"set an alarm", AlarmCommandPath},
    {"send an email", EmailCommandPath},
    {"", NULL}
};

将按如下方式使用:

CommandPath *cPath = NULL;
string input;
getline(cin, input);
for(int i = 0; cPathLookup[i] != ""; i++) {
        if(cPathLookup[i].command == input)
                cPath = new cPathLookup[i].cPath;
}

显然,这段代码毫无意义,但我认为我的意图很明显 - 根据输入,我希望将 cPath 初始化为新的 AlarmCommandPath 或新的 EmailCommandPath。我可以使用一个根据输入返回实例的函数来处理它,但是整个 ifs 序列似乎并不优雅。

我还应该注意,AlarmCommandPath 和 EmailCommandPath 是从 CommandPath 派生的,并且 CommandPath 是一个抽象类。

感谢您提供的任何帮助。

编辑:我刚刚注意到,尽管 CommandPath 是抽象的,但我有一个声明:

CommandPath *cPath = NULL;

在工作代码中。为什么会编译?

【问题讨论】:

    标签: c++ class inheritance pointers


    【解决方案1】:

    您不能在结构中存储类型,但可以存储指向创建类型的函数的指针。

    CommandPath * CreateEmail() {
         return new EmailCommandPath;
    }
    
    CommandPath * CreateAlarm() {
         return new AlarmCommandPath;
    }
    

    那么你的结构看起来像:

    typedef Command * (* CreateFunc)();
    struct MyMap {
       string command;
       CreateFunc func;
    };
    

    还有地图:

    MyMap m[] = {{"email", CreateEmail }, {"alarm", CreateAlarm}};
    

    然后你像以前一样查找索引 i,并使用它:

    CommandPath * p = m[i].func():
    

    您可以创建指向抽象类型的指针 - 您不能创建它们的实例。

    【讨论】:

      【解决方案2】:

      AlarmCommandPath 和 EmailCommandPath 是从 COMmandPath 派生的,对吗?

      在这种情况下,您不能将 AlarmCommandPath/EmailCommandPath 的实例分配给 CommandPath - 这在技术上是可行的,但它不会满足您的要求。实例 无论您为它分配什么,CommandPath 都将保持为 CommandPath 的一个实例(它将具有 CommandPath 的虚函数表)。

      您需要使用工厂方法(将返回 CommandPath* 的函数)。类似的东西:

      struct A{
      };
      
      struct B: public A{
      };
      
      struct C: public A{
      };
      
      A* factoryA(){
          return new A();
      }
      
      A* factoryB(){
          return new B();
      }
      
      A* factoryC(){
          return new C();
      }
      
      typedef A* (*FactoryMethod)();
      
      struct{
          const char* command;
          FactoryMethod factoryMethod;
      } factoryTable[] = {
          {"A", factoryA},
          {"B", factoryB},
          {"C", factoryC},
          {0,0}
      };
      

      【讨论】:

      • 这可以在没有堆分配的情况下完成吗?我的意思是,这些工厂方法可以在堆栈上构造一个 A 或 B 或 C 并按值返回吗?
      【解决方案3】:

      我假设您正在尝试实现表查找来替代系统中的大型 if\else 语句。

      为了清楚起见,我会改用工厂设计模式。如果在许多地方围绕您的代码重复,那么拥有大型 if/else 逻辑只会非常糟糕。只要它在一个地方,即工厂,那么在我看来,你的设计就很好。

      【讨论】:

        【解决方案4】:

        就我个人而言,如果您只有 1 个工厂来为其接收的字符串的不同值创建不同的“CommandPaths”,我个人认为这不是一个大问题。无论如何,您的代码将无法工作,因为您无法按照您尝试的方式存储类型。

        如果我必须这样做,那么我会使用指向工厂函数的函数指针并使用 std::map 将字符串映射到这些,就像这段代码中所示的那样,并且可能将指针包装在适当的 smart-指针,而不是使用原始指针:

        #include <string>
        #include <map>
        
        struct A {
        };
        
        struct B : public A {
        };
        
        struct C : public A {
        };
        
        A *BFactory(){
            return new B();
        }
        
        A *CFactory(){
            return new C();
        }
        
        typedef A *(*Factory)();
        typedef std::pair<std::string,Factory> Pair;
        typedef std::map<std::string,Factory> Map;
        
        Pair Pairs[] = 
        {
            std::make_pair( "alarm", BFactory ),
            std::make_pair( "email", CFactory )
        };
        
        Map Lookup( Pairs, Pairs + sizeof(Pairs)/sizeof(*Pairs) );
        
        A *CreateInstance( const std::string &Type )
        {
            Map::const_iterator i = Lookup.find( Type );
        
            if( i != Lookup.end() )
                return i->second();
            else
                return 0;
        }
        

        至于你关于指针和抽象类的问题,你可以有一个指向抽象类的指针,但你不能实例化一个抽象类。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-07
          • 1970-01-01
          • 2012-03-03
          • 2019-09-03
          • 1970-01-01
          相关资源
          最近更新 更多