SORU
24 ŞUBAT 2009, Salı


Bir yolu, bir dize kendi sınıf adı tutarak nesneleri oluşturmak için var mı?

Elimde bir dosya var: Baz.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

ve başka bir dosya: BaseFactory.h

#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/

Yok bir yolu, bir şekilde bu dize dönüştürmek için bir tip (sınıf), böylece BaseFactory olmazdı bilmek Olası Türetilmiş sınıfları ve (varsa) her biri? Bu dizeden bir sınıf oluşturabilir miyim?

Bu C yapılabilir bence# Yansıma. C benzer bir şey var mı ?

CEVAP
24 ŞUBAT 2009, Salı


Hayır, eşleme Kendin yapmadığın sürece yok. C zamanında belirlenmiş nesneleri oluşturmak için bir mekanizma vardır. Kendini eşleme yapmak için bir harita olsa da kullanabilirsiniz:

template<typename T> Base * createInstance() { return new T; }

typedef std::map<std::string, Base*(*)()> map_type;

map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;

Ve sonra yapabilirsiniz

return map[some_string]();

Yeni bir örneğini almak. Başka bir fikir türleri kendilerini kayıt için:

// in base.hpp:
template<typename T> Base * createT() { return new T; }

struct BaseFactory {
    typedef std::map<std::string, Base*(*)()> map_type;

    static Base * createInstance(std::string const& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return 0;
        return it->second();
    }

protected:
    static map_type * getMap() {
        // never delete'ed. (exist until program termination)
        // because we can't guarantee correct destruction order 
        if(!map) { map = new map_type; } 
        return map; 
    }

private:
    static map_type * map;
};

template<typename T>
struct DerivedRegister : BaseFactory { 
    DerivedRegister(std::string const& s) { 
        getMap()->insert(std::make_pair(s, &createT<T>));
    }
};

// in derivedb.hpp
class DerivedB {
    ...;
private:
    static DerivedRegister<DerivedB> reg;
};

// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");

Kayıt için bir makro oluşturmak için karar verebilir

#define REGISTER_DEC_TYPE(NAME) \
    static DerivedRegister<NAME> reg

#define REGISTER_DEF_TYPE(NAME) \
    DerivedRegister<NAME> NAME::reg(#NAME)

Bu ikisi için de daha iyi isimler vardır eminim. Muhtemelen sense burada kullanmak için yapar başka bir şey shared_ptr.

Eğer ortak bir temel sınıf olan alakasız türleri bir dizi varsa, işlev işaretçisi bir dönüş boost::variant<A, B, C, D, ...> türü yerine verebilirsiniz. Eğer bir sınıf Foo, Bar ve Baz varsa, bu gibi görünüyor:

typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() { 
    return variant_type(T()); 
}

typedef std::map<std::string, variant_type (*)()> map_type;

boost::variant bir birlik gibi. Nesne veya başlatılıyor atama için kullanılan bakarak içinde saklı olan bunu bilir. Belgeleri here bir göz at. Son olarak, ham bir işlev işaretçisi kullanımı da biraz yaşlıca. Modern C kodu belirli işlevleri / tür ayrılmış olmalıdır. Boost.Function daha iyi bir yol için bakmak isteyebilirsiniz. Böyle (harita) görünecektir:

typedef std::map<std::string, boost::function<variant_type()> > map_type;

std::function std::shared_ptr C dahil olmak üzere bir sonraki sürümü de mevcut olacak.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Justin Davis

    Justin Davis

    14 Ocak 2008
  • listedabive

    listedabive

    30 Ocak 2007
  • Crossover

    Crossover

    18 HAZİRAN 2007