Класс поиска структуры массива в C ++ - PullRequest
1 голос
/ 15 мая 2010

Я пытаюсь создать структурный массив, который связывает входные строки с классами следующим образом:

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;

в рабочем коде. Почему это компилируется?

Ответы [ 4 ]

1 голос
/ 15 мая 2010

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}
};
1 голос
/ 15 мая 2010

Вы не можете сохранить тип в структуре, но вы можете сохранить указатель на функцию, которая создает тип.

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():

И вы можете создавать указатели на абстрактные типы - вы не можете создавать их экземпляры.

0 голосов
/ 15 мая 2010

Лично я не вижу большой проблемы, если у вас есть только одна фабрика для создания разных «CommandPaths» для разных значений получаемой строки. В любом случае, ваш код не будет работать, потому что вы не можете хранить типы так, как пытаетесь.

Если бы мне пришлось это сделать, то для одного я бы использовал указатели на функции к фабричным функциям и использовал бы std :: map для сопоставления строк с ними, как показано в этом коде, и, возможно, обернул бы указатели в соответствующие интеллектуальные указатель, вместо использования необработанных указателей:

#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;
}

Что касается вашего вопроса об указателях и абстрактных классах, у вас может быть указатель на абстрактный класс, но вы не можете создать экземпляр абстрактного класса.

0 голосов
/ 15 мая 2010

Я предполагаю, что вы пытаетесь реализовать поиск в таблице в качестве замены большого оператора if \ else в вашей системе.

Для ясности я бы вместо этого использовал шаблон дизайна фабрики. Наличие большой логики if / else действительно плохо, если она повторяется вокруг вашего кода во многих местах. Пока он находится в одном месте, то есть на фабрике, то, на мой взгляд, у вас хороший дизайн.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...