2012-06-05 24 views
7

W jaki sposób można wydrukować pochodną nazwę klasy z klasy bazowej bez łączenia konstruktorów do końca. Innymi słowy, czy można to zrobić ściśle z klasy bazowej bez dodawania kodu w każdej klasie pochodnej?Drukowanie nazwy klasy pochodnej w klasie bazowej

To jest przykład tego, co mam, a jeśli istnieje sposób, chciałbym pozbyć się łańcucha konstruktora.

EDYCJA: Idealnie szukam czegoś do dodania do klasy bazowej bez konieczności edycji wszystkich klas pochodnych. W tym momencie mój prawdziwy kod ma ~ 17 klas (z potrzebą więcej), więc coś, co mogłoby wykonać pracę prosto z klasy bazowej byłoby idealne. Nawet jeśli jest to specyficzne dla kompilatora (g ++ lub clang).

#include <iostream> 

class Base { 
public: 
    Base(std::string id) { 
      std::cout<<"Creating "<<id<<std::endl; 
    } 
}; 

class Child : Base { 
public: 
    Child(std::string id) : Base(id) {} 
    Child() : Base(typeid(this).name()) {} 
}; 

class GrandChild : Child { 
public: 
    GrandChild(std::string id) : Child(id) {} 
    GrandChild() : Child(typeid(this).name()) {} 
}; 

class GrandGrandChild : GrandChild { 
public: 
    GrandGrandChild(std::string id) : GrandChild(id) {} 
    GrandGrandChild() : GrandChild(typeid(this).name()) {} 
}; 



int main() { 
    GrandGrandChild *A = new GrandGrandChild(); 
    GrandChild *B = new GrandChild(); 
    Child *C = new Child(); 

    return 0; 
} 

która drukuje:

Creating GrandGrandChild 
Creating GrandChild 
Creating Child 

Ale z skompilowany dodanego prefiksu.

+1

Użyj polimorfizmu, a nie RTTI, do czego służy polimorfizm. – Griwes

+0

Zamierzam używać go wyłącznie do celów debugowania, dlatego warto rzucić coś na podstawową klasę. – NFA

+2

nie dynamicznie tworzy instancji obiektów, np. "GrandGrandChild A;" byłoby całkowicie wystarczające do oceny domyślnego konstruktora. – moooeeeep

Odpowiedz

9

Nie ma niestety łatwego rozwiązania.

Problemem jest to, że budowa obiektów polimorficznych jest dość skomplikowany, w tej chwili jesteś budującej Base podsekcji klasy Child, budujesz Base nadal, a nie Child (bo próbuje uzyskać dostęp Child członków byłoby nie- sensowne, nie zostały jeszcze zbudowane!)

Jako takie, wszystkie sposoby pobierania informacji dynamicznych (znane jako RTTI lub Informacje o Typie RunTime) są dobrowolnie zablokowane , aby zapobiec takiemu błędowi.

Ze względów symetrycznych to samo występuje w destruktorze.


Teraz tylko konstruktor i destruktor są tak zablokowane, dlatego można doskonale mieć metodę name() że chętnie zwrotu prawdziwą nazwę dynamicznego typu instancji we wszystkich innych przypadkach:

class Base { 
public: 
    std::string name() const { return typeid(*this).name(); } 
}; 

Będzie działać ... chyba że wywołasz go z konstruktora lub destruktora, w takim przypadku zgłosi typ statyczny.

Teraz, jeśli chodzi o "dziwaczne" dane wyjściowe, każda implementacja (kompilator) może tutaj dostarczać własne dane wyjściowe (i nie muszą nawet być różne dla różnych typów, szalone, eh!). Wydaje się, że używasz gcc lub klang.

Istnieje demanglers do interpretowania takich wyników, lub jeśli twój program jest dość prosty i ich interfejs cię przeraża, możesz po prostu spróbować przeanalizować go ręcznie, aby usunąć cruft. Nazwa klasy powinna pojawić się w pełni, będzie po prostu poprzedzona pewnymi nonsensem (zasadniczo przestrzenie nazw i liczby).

+0

Rozumiem. Naprawdę nie znalazłem tego, czego szukałem, gdy szukałem informacji w Google, więc obawiałem się, że nie da się tego rozwiązać w sposób, w jaki chciałbym to zrobić. Po prostu wierzyłem, że informacje będą dostępne dla kompilatora, więc nawet jeśli buduje on część klasy podstawowej, przejdzie on z poziomu najwyższego poziomu, aby mógł zachować te informacje. – NFA

0

Ponieważ wskazuje to na debugowanie, można polegać na dziedziczeniu wirtualnym, aby uniknąć podania nazwy przez wszystkie pośrednie klasy pochodne, a zamiast tego przekazać ją bezpośrednio do Base. Ponadto można zmodyfikować Base, aby pobrać konstruktor szablonu, aby uprościć elementy dla klas pochodnych.

class Base { 
public: 
    template <typename DERIVED> 
    Base (DERIVED *d) { 
     std::cout << "Creating " << typeid(*d).name() << std::endl; 
    } 
}; 

class Child : virtual public Base { 
public: 
    Child() : Base(this) {} 
}; 

class GrandChild : public Child, virtual public Base { 
    GrandChild() : Base(this) {} 
} 

class GrandGrandChild : public GrandChild, virtual public Base { 
    GrandGrandChild() : Base(this) {} 
} 
+0

Nie potrzebujesz 'virtual' na dziedziczeniu z' Child' i 'GrandChild', tylko w dziedziczeniu z' Base'. – ymett

+0

@ymett: dziękuję, edytuj. – jxh

+0

W tej chwili mam ~ 17 klas (oczekuję, że potrzebuję więcej), które rozgałęziają się z jednej klasy bazowej. Dlatego szukam czegoś, co mógłbym łatwo dodać do klasy bazowej i nie edytować wszystkich klas indywidualnie. Może to, o co pytam, nie jest możliwe ... – NFA

0

można podać funkcję inicjalizacji, która musi zostać wywołana z każdego konstruktora.

class Base { 
protected: 
    Base() { init(typeid(this).name()); } 
    void init(std::string id) { 
    std::cout<<"Creating "<<id<<std::endl; 
    } 
}; 

jakoś trzeba się upewnić, że późniejsze inits będzie bezpiecznie zastępują zmiany poprzednich:

Creating P4Base 
Creating P5Child 
Creating P10GrandChild 
Creating P15GrandGrandChild 
Creating P4Base 
Creating P5Child 
Creating P10GrandChild 
Creating P4Base 
Creating P5Child 

mam zamiar go używać wyłącznie do celów debugowania, który jest dlaczego coś do rzucenia w klasę podstawową byłoby wygodne.

Czy rozważałeś dodanie makra do swojego kodu, aby wydrukować dane wyjściowe debugowania?

#ifdef DEBUG 
    #define PRINT_CLASSNAME std::cout<<"Creating "<<id<<std::endl; 
#else 
    #define PRINT_CLASSNAME ; 
#endif 

Musisz dodać go do swoich konstruktorów tylko raz, ale jeśli chcesz go wyłączyć (tymczasowo), po prostu go nie zdefiniujesz?

+0

Powoduje to usunięcie łańcucha konstruktora, ale nadal muszę mieć kod w każdej klasie pochodnej. Szukałem jakiegoś rozwiązania, które po prostu wrzuciłoby do klasy bazowej. Wydaje się, że to informacja, którą znałby kompilator. Byłbym nawet w porządku z kodem specyficznym dla kompilatora (g ++/clang), gdyby to mi pomogło. – NFA

+0

@NFA wydaje się, że chcesz dodać niektóre funkcje wyjściowe debugowania, które chcesz usunąć tak proste, jak to możliwe, gdy przestaniesz go potrzebować. Zobacz moją edycję dla innej sugestii. – moooeeeep

+0

@moooeeeep na swój sposób pojawi się Tworzenie bazy, tworzenie elementu potomnego dla jednej klasy, a gdy widzisz wynik, nie można go łatwo odróżnić od dwóch klas. –

Powiązane problemy