2010-06-24 2 views
3

클래스가 Image 인 것으로 가정합니다. 파싱 ​​중 어떤 시점에서 적절한 시간에 "Image"가 읽혀집니다. 즉, 클래스 Image의 객체를 만들고 싶습니다.문자열을 기반으로 객체 생성

내가 고려중인 것은이 문자열을 생성자 호출에 적절한 클래스로 매핑하는 것이지만이를 수행하는 방법을 잘 모르겠습니다.

e.e.

container.push_back(some_map[stringParsedIn]); // basic idea 
+1

이 질문의 구문 분석에 문제가 있습니다. –

+0

가능한 복제본 [해당 클래스 이름을 포함하는 문자열에서 개체를 인스턴스화하는 방법이 있습니까?] (http://stackoverflow.com/questions/582331/is-there-a-way-to-instantiate-objects-from- a-string-holding-their-class-name) – hadley

답변

0

당신은 공장 기능을 설명하고 있습니다. 레지스트리에서 간단한 if/else 체인까지 여러 가지 방법으로이를 수행 할 수 있습니다.

일반적으로 Image 클래스는 다른 "파싱 된"유형과 비슷한 기본 클래스에서 파생됩니다. 그렇게하면 같은 컨테이너에 모두 추가 할 수 있습니다. 당신은 일반적으로 매크로를 사용하는 것이 CreateMedia 대신, 공장을

Media *CreateMedia(const string &type) { 
    if (type == "Image") { 
    return new Image; 
    } else if (type == "Sound") { 
    return new Sound; 
    } else { 
    // handle invalid type error 
    } 
} 

또 다른 대안은 레지스트리를 사용하는 것입니다

class Media { 
public: 
    virtual Save() = 0; 
}; 

class Image : public Media { 
public: 
    Image() { } 
    virtual Save() { ... } 
}; 

class Sound : public Media { 
public: 
    Sound() { } 
    virtual Save() { ... } 
}; 

가장 간단한 구조는 공장 기능은 다음과 같습니다

이 계층 구조를 상상/레지스트리 및 하위 메커니즘을 만드는 몇 가지 메커니즘 :

// This is some mechanism to create types of Media. 
template <typename T> 
struct CreatorFunction { 
    Media *operator() { 
    return new T; 
    } 
}; 

// This is the factory that the types will register with. 
class Factory { 
public: 
    // singleton access function. 
    static Factory* Get() { 
    static Factory* f = new Factory; 
    return f; 
    } 

    // Creates Media of the given type. 
    Media* Create(const string& name) { return registry_[name](); } 

    // Records 'name' with the creator function 'func'. 
    void Add(const string& name, const CreatorFunction &func) { 
    registry_.insert(name, func); 
    } 
private: 
    Factory() { } // users can't create factories, they can only use singleton. 
    hash_map<string, CreatorFunction> registry_; 
}; 

#define REGISTER_MEDIA(type) Factory::Get()->Add(#type, CreatorFunction<type>); 

REGISTER_MEDIA(Image); // usually goes with the Image class. 
REGISTER_MEDIA(Sound); // usually goes with the Sound class. 

int main(int argc, char** argv) { 
    string parsedIn = "Image"; 
    Factory::Get()->Create(parsedIn); 
} 

이것은 전반적으로보다 깔끔한 접근법이지만 링커가 심볼이 사용되지 않는다고 생각하고 바이너리에서 등록 된 중요한 클래스를 트리밍하는 데 문제가있을 수 있습니다. 더 복잡한 것을 필요로 할 때까지 if/then 체인을 계속 사용하고 싶을 것입니다. 모든 하위 유형이 정의 된 단일 위치를 갖는 것이 불가능하면 일반적으로 레지스트리로 이동합니다.

+0

'REGISTER_MEDIA'의 정의에서'# Image'는 오타입니까? 그것은'# type'이어야합니다, 맞습니까? – bstpierre

+0

네, 물론 편집은 거부되었습니다. – wmamrak

+0

가 지금 고정되었습니다. 감사! – Stephen

0

Image *createImage() { 
    return new Image(); 
} 

그런 다음지도에서이 함수에 대한 포인터를 저장할 수, 당신은 생성자로 함수 포인터를 저장할 수 없습니다,하지만 당신은 새롭게 구축 된 객체를 반환하는 함수에 대한 포인터를 저장할 수 .

std::map<std::string, Image *(*)()> constructorMap; 
constructorMap.insert(std::pair<std::string, Image *(*)()>("Image", createImage)); 

그리고 내가 100 % 당신이 요구하고있는 무슨

Image *myImage = constructorMap["Image"](); 
0

로 부르지 만, 나는 그것을 추측을 줄 것이다.

Image* makeImage(ArgType arg) { return new Image(arg); } 

을 그리고 당신은 당신의지도에 함수 포인터를 저장할 수 있습니다 :

당신은 기능의 생성자를 포장 할 수!

map["Image"] = makeImage; 

나중에 전화 해주세요.

SuperclassOfImage soup = map["Image"](arg); 

물론 여기에 제한은 함수의 타입 서명이 동일한 유형의 인수를해야하며 동일한 유형 (이미지 또는 이미지의 부모 중 하나는 클래스의 인스턴스)을 반환해야한다는 것입니다.

2

Stephen이 지적한 것처럼 Factory 패턴 (Image는 추상 기본 클래스라고 가정)을 설명합니다. 그러나 if/else 문으로 구성된 커다란 함수 대신 설명 된대로 문자열을 작성 함수와 연관시키는 것이 도움이 될 수 있습니다. 이미지 하위 클래스를 모두 같은 방식으로 구성 할 수 있다고 가정하면 다음과 같은 한 가지 방법을 사용할 수 있습니다.

typedef Image* create_image_function(); 

template <class T> 
Image* create_image(SomeType arg) 
{ 
    return new T(arg); 
} 

... 
map<string, create_image_function*> creators; 
creators["Foo"] = &create_image<Foo>; 
creators["Bar"] = &create_image<Bar>; 
creators["Baz"] = &create_image<Baz>; 

shared_ptr<Image> ImageFactory::make_image(const string& str) 
{ 
    // checking to see if str exists as a key 
    // would be nice 
    return shared_ptr<Image>(creators[str](arg)); 
}