2009-09-24 18 views
26

Zastanawiam się, czy możliwe jest przekazanie klasy jako parametru w języku C++. Nie przekazując obiektu klasy, ale samą klasę, która pozwoli mi używać tej klasy w ten sposób.C++ Przekazywanie klasy jako parametru

void MyFunction(ClassParam mClass) 
{ 
    mClass *tmp = new mClass(); 
} 

Powyższe nie jest rzeczywistym kodem, ale mam nadzieję, że wyjaśnia, co próbuję zrobić w przykładzie.

Odpowiedz

37

Można używać szablonów, aby osiągnąć coś podobnego (ale nie dokładnie to):

template<class T> 
void MyFunction() 
{ 
    T *tmp = new T(); 
} 

i nazywają go MyFunction<MyClassName>().

Należy zauważyć, że w ten sposób nie można użyć "zmiennej" w miejsce T. Powinien być znany podczas kompilacji.

19

C++ nie przechowuje metadanych dotyczących klas, tak jak robią to inne języki. Zakładając, że zawsze używać klasy z konstruktora bez parametrów, można użyć szablonów, aby osiągnąć to samo:

template <typename T> 
void MyFunction() 
{ 
    T* p = new T; 
} 
+0

Podobnie jak inne języki Nie wszystkie języki przechowują dane meta dotyczące programu. Jest to stosunkowo nowa funkcja współczesnych języków. –

+1

@Martin, jest to interesująca definicja "stosunkowo nowego", biorąc pod uwagę, że Smalltalk, który był dość popularny w swoim czasie, i miał w nim sporo kodu, zrobił to właśnie w latach 70. :) –

+1

Podobnie jak wiele innych rzeczy przypisane do Smalltalk, Lisp istniał tam w latach pięćdziesiątych - cały program był strukturą, którą można było sprawdzić i zmanipulować. –

0

Można tworzyć statyczne metody fabryki w swojej klasie (-y), że po prostu zwraca nową instancję klasy, a następnie możesz przekazać wskaźniki do tej funkcji, podobnie jak to, co chcesz zrobić w swoim przykładzie. Typy zwrotów są kowariantne, więc jeśli wszystkie twoje klasy implementują ten sam interfejs, możesz zwrócić wskaźnik funkcji do tego interfejsu. Jeśli nie wszystkie mają wspólny interfejs, prawdopodobnie pozostaniesz z powracaniem void *. Tak czy inaczej, jeśli chcesz użyć konkretnej podklasy, musisz dynamic_cast.

2

Można również przekazać wskaźnik funkcji, który po wywołaniu tworzy instancję dowolnego elementu i zwraca ją.

void MyFunction(ClassCreatorPtr makeClassFn) 
{ 
    void * myObject = makeClassFn(); 
} 

Musisz go zwrócić wskaźnik do klasy bazowej, aby zrobić coś naprawdę interesującego.

0

Alternatywą dla szablonów jest użycie zamknięcia lambda z C++ 11. Oto moje preferencje.

// in header file 
IClass * MyFunctionThatDoesStuff(const IParams & interface_params, 
    std::function<IClass * (const IParams & interface_params)> cls_allocator); 

// in source file 
IClass * MyFunctionThatDoesStuff(const IParams & interface_params, 
    std::function<IClass * (const IParams & interface_params)> cls_allocator) { 
    // Some processing. Perhaps the interface_params are generated 
    // inside this function instead of being passed to it. 
    IClass * mCls = cls_allocator(interface_params); 
    // Do whatever with mCls 
    return mCls; 
} 

// Somewhere else in the code. 
{ 
    Param1Type param1 = whatever1; 
    Param2Type param1 = whatever2; 
    // param1, param2, etc. are parameters that only 
    // SomeClsDerivedFromIClass constructor knows about. The syntax &param1 
    // achieves the closure. 
    // interface_param1 is common to all classes derived from IClass. 
    // Could more than one parameter. These parameters are parameters that 
    // vary from different calls of MyFunctionThatDoesStuff in different 
    // places. 
    auto cls_allocator = 
     [&param1, &param2](const IParams & interface_params)->IClass * { 
      return new SomeCls1DerivedFromIClass(interface_params, 
       param1, param2); 
     }; 
    IClass * mCls = MyFunctionThatDoesStuff(interface_params, 
     cls_allocator); 
} 

// Somewhere else in the code again. 
{ 
    ParamXType paramX = whateverX; 
    ParamYType paramY = whateverY; 
    auto cls_allocator = 
     [&paramX, &paramY](const IParams & interface_params)->IClass * { 
      return new SomeCls2DerivedFromIClass(interface_params, 
       paramX, paramY); 
     }; 
    IClass * mCls = MyFunctionThatDoesStuff(interface_params, 
     cls_allocator); 
} 

Powyższy pomysł kodu działa dobrze w przypadku szybkiego wzorca budowania lub odmiany wzoru fabrycznego. Metoda lambda jest w zasadzie metodą fabryczną. Aby uczynić go jeszcze bardziej dynamicznym, możesz użyć auto do wpisywania parametrów. Coś takiego.

auto * MyFunctionThatDoesStuff(const auto & interface_params, 
    std::function<auto * (const auto & interface_params)> cls_allocator); 

Przychodzę do tego z wpływu Pythona, w którym można po prostu przekazać typ klasy do funkcji.

Powiązane problemy