Доступ к классам C ++ на основе списка строк - PullRequest
1 голос
/ 05 октября 2010

Я надеюсь, что это возможно в C ++.

У меня есть список классов, которые наследуются от общего базового класса (Base). Все они будут скомпилированы как часть библиотеки, связанной с моей программой.

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

Когда моя программа запускается, она собирает вектор всех классов (используя какой-то метод предварительной обработки, который я уже реализовал), которые реализуются из Base.

Что я хочу сделать (вы, наверное, уже догадались), это создавать объекты из этих классов только на основе имен строк. Возможно ли это?

Привет

Mark

Ответы [ 3 ]

2 голосов
/ 05 октября 2010

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

#include <string>

using namespace std;

class b1{};

class d1 : public b1{};

class d2 : public b1{};

...

int main(int argc, char* argv[]) {
   string name(argv[1]);

   b1* b;

   if (name == "d1")
      b = new d1;
   else if (name == "d2")
      b = new d2;
}
2 голосов
/ 05 октября 2010

Что ж, если у вас есть предварительно обработанный список всех классов, вы можете создать строительный объект, который будет «знать» каждый из этих классов и будет создавать (путем ручного поиска в списке) их по запросу.

0 голосов
/ 05 октября 2010

Конечно, но не так, как хотелось бы. C ++ не имеет никакого отражения, поэтому вы должны создать механизм самостоятельно: (см. Ниже для EDIT, который реализует карту указателей фабрики)

#include <string>
#include <vector>
#include <memory>
using namespace std;

class Base
{
};

class Dog : public Base
{
};

class Cat : public Base
{
};

int main()
{
    typedef vector<string> strings;
    strings names;
    names.push_back("Dog");
    names.push_back("Cat");
    names.push_back("Dog");
    names.push_back("Cat");

    typedef vector<Base*> Bases;
    Bases my_objs;

    for( strings::const_iterator it = names.begin(), it_end = names.end(); it != it_end; ++it )
    {
        if( *it == "Dog" )
            my_objs.push_back(new Dog);
        else if( *it == "Cat" )
            my_objs.push_back(new Cat);

    }
}

EDIT:

Любое решение, которое вы придумали в C ++, будет в корне некоторой разновидностью вышеупомянутого. Один из распространенных вариантов - попытаться избавиться от блока if, выполнив поиск. Это может быть реализовано с использованием объекта map, который связывает имя объекта с функцией, которая его создает. В этом подходе следует отметить, что указатели функций на карте должны иметь одинаковую сигнатуру, то есть фабричные методы Dog и Cat должны принимать одинаковое количество и тип параметров. Эта проблема может быть решена с помощью Boost::Any ( ссылка ) или другими героическими методами, но это выходит за рамки этого поста.

Вот решение, которое реализует map указателей метода фабрики:

#include <string>
#include <vector>
#include <map>
using namespace std;

class Base
{
public:
    virtual ~Base()  {};
};

class Dog : public Base
{
public:
    static Base* make_dog() { return new Dog; }

};

class Cat : public Base
{
public:
    static Base* make_cat() { return new Cat; }
};

typedef Base*(*ObjFactoryFn)();
typedef map<string, ObjFactoryFn> Factories;


int main()
{
    // set up the map of object factory pointers
    Factories factories;
    factories["Dog"] = &Dog::make_dog;
    factories["Cat"] = &Cat::make_cat;

    // build list of objects we want
    typedef vector<string> strings;
    strings names;
    names.push_back("Dog");
    names.push_back("Cat");
    names.push_back("Dog");
    names.push_back("Cat");

    // build objects using the factory map
    for( strings::const_iterator it = names.begin(), it_end = names.end(); it != it_end; ++it )
    {
        // construct
        Base* my_obj = factories[*it]();    // this should use map's "find" method for robustness
        // destroy
        delete my_obj;
    }

}
...