crtp & vererbung

Du bist neu in der Welt von C++? Dann schau hier herein!
Antworten
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

crtp & vererbung

Beitrag von Aenni »

Hallo zusammen, ich habe ne kleine Frage zu crtp:
http://en.wikipedia.org/wiki/Curiously_ ... te_pattern

ich verwende crtp um mir meine erzeugten und "lebenden" objekte anzuzeigen.

jetzt dachte ich es wäre schön wenn die template Klasse crtp als basis klasse dient und ich entsprechend vererben kann.
Grundgedanke vom design:
CObjectCounter (crtp klasse) als basis klasse
CLogging erbt von CObjectCounter
alle weiteren Klassen erben von CLogging

soviel zur Theorie, das Problem ist schnell ersichtlich, das zaehlen der Objekte kann nicht mehr richtig funktionieren. Jetzt würde mich interessieren ob es eine Möglichkeit gibt dass ich die CObjectCounter klasse dennoch als basisKlasse verwenden kann, ohne, dass jede Klasse explizit von CObjectCounter erbt und zusaetzlich von CLogging.

Ich hoffe ich konnte mich soweit verstaendlich ausdruecken, die ObjectCounter class verwende ich so wie im wiki artikel beschrieben, daher spare ich mir aufgrund der uebersicht das copy pasten des codes.

Vielen Dank für eure Anregungen.
Gruss
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Du musst es einfach schaffen, dass für jede Unterklasse eine eigene Spezialisierung von counter existiert. Die einfachste Lösung wäre, den Logger selber zu einem template zu machen:

Code: Alles auswählen

#include <iostream>
#include <string>

template <typename T>
class counter {
    static int objects_created;
    static int objects_alive;
public:
    counter()
    {
        ++objects_created;
        ++objects_alive;
    }
    
    static void print_stat() {
        std::cout << objects_created << " " << objects_alive << std::endl;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
        --objects_alive;
    }
};
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );


template <typename T>
class logger : public counter<T>
{
    std::string last_log_;
public:
    void log(std::string const& msg) {
        last_log_ = msg;
    }
};

class test1 : public logger<test1> {
};

class test2 : public logger<test2> {
};


int main() {
    for(int i=0; i<10; ++i) {
        test1 t;
        if(i%2) {
            test2 t2;
            test1 *t1 = new test1;
        }
    }
    test1::print_stat();
    test2::print_stat();
}
Allerdings stell ich mir die Frage, was es bringt von einem logger zu ERBEN. Normalerweise wird der VERWENDET. Hast du da was spezielles vor?
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

Hi Franzf,

danke fuer deinen beitrag, das logging als template klasse zu machen ist natuerlich sinfrei, wie du schon geschrieben hast, will man die log klasse ja verwenden ....
danke, ein weiere sinvolles beispiel waere z.b die rechteverwaltung in einem programm -was mir so auf anhieb einfallen wuerde ;)

merci nochmal !
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Code: Alles auswählen

class empty {
};
typedef logger<empty> my_logger;
:P
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

hallo, nochmal ich,

mir ist gerade was aufgefallen, wie ich ein Problem bekomme wenn eine Klasse von einer anderen klasse erbt und diese Vater klasse schon von der counter klasse erbt.
Zur verdeutlichung habe ich das ganze mit der klasse test3 erweitert. diese klasse test3 erbt von test1. beim instanzieren der klasse test3 wird natuerlich der counter verhaun. und die falsche anzahl von objekte ausgegeben.

Es muss doch machbar sein, dass die unterklassen die Spezialiserung von counter bekommen.
Ich bleib mal dran, wenn einer ne idee hat waere ich dankbar, ansosntne poste ich meine ergebnisse.

Danke!

[quote="franzf"]Du musst es einfach schaffen, dass für jede Unterklasse eine eigene Spezialisierung von counter existiert. Die einfachste Lösung wäre, den Logger selber zu einem template zu machen:

Code: Alles auswählen

#include <iostream>
#include <string>

template <typename T>
class counter {
    static int objects_created;
    static int objects_alive;
public:
    counter()
    {
        ++objects_created;
        ++objects_alive;
    }
    
    static void print_stat() {
        std::cout << objects_created << " " << objects_alive << std::endl;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
        --objects_alive;
    }
};
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );


template <typename T>
class logger : public counter<T>
{
    std::string last_log_;
public:
    void log(std::string const& msg) {
        last_log_ = msg;
    }
};

class test1 : public logger<test1> {
};

class test2 : public logger<test2> {
};

[b]class test3 : test1{
};[/b]

int main() {
    for(int i=0; i<10; ++i) {
        test1 t;
       [b] test3 t3;[/b]
        if(i%2) {
            test2 t2;
            test1 *t1 = new test1;
        }
    }
    test1::print_stat();
    test2::print_stat();
    test3::print_stat();
}
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Das Problem ist genau das gleiche wie in deinem ersten Post. Grund: counter<test1> ist eine Instantiierung des Klassentemplates, counter<test2> eine andere. test3 bekommt aber keine eigene, sondern verwendet die von test1, also counter<test1>.
Das Problem ist etwas komplizierter, da du es schaffen musst, bei einer Vererbung das Zählen im Konstruktor/Destruktor der Basisklasse zu unterbinden. Dazu muss ich auch erstmal nachdenken.
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

franzf hat geschrieben:Das Problem ist genau das gleiche wie in deinem ersten Post. Grund: counter<test1> ist eine Instantiierung des Klassentemplates, counter<test2> eine andere. test3 bekommt aber keine eigene, sondern verwendet die von test1, also counter<test1>.
Das Problem ist etwas komplizierter, da du es schaffen musst, bei einer Vererbung das Zählen im Konstruktor/Destruktor der Basisklasse zu unterbinden. Dazu muss ich auch erstmal nachdenken.
danke fuer deine muehen!

gruß
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Kern des Problems ist ja, wie in C++ Objekte auf- und abgebaut werden, wie der Reihe nach Konstruktoren- und Destruktoren aufgerufen werden. Da muss also gesteuert werden, wer wann wie zählt. In jedem Falle würde ich den Counter außerhalb der Objekte halten, und immer per ref()/unref() hoch/runterzählen (wir haben ja jetzt keinen Konstruktor/Destruktor mehr).
Ich sehe 2 Möglichkeiten:
1) Zum konstruieren einen public und einen protected-Konstruktor. Der Public macht ein ref() auf den Zähler, der protected nicht. Beim Zerstören gibts nen bool (der muss natürlich bereits in der untersten Klasse vorhanden sein, also am besten eine Basisklasse Countable einführen die den Member hält), der gecheckt wird. Ist der true gibts ein unref() auf den Zähler und der bool wird auf false gesetzt.
2) Geht ohne Basisklasse Countable, dafür muss jede Klasse eine create() und eine destroy() anbieten, in denen ref() und unref() auf den Counter aufgerufen wird.

Vorteile/Nachteile:
1) Großer Nachteil der zusätzliche Speicherbedarf PRO OBJEKT, dafür geht es auch schön mit automatischer Speicherverwaltung ("Stack"). Durch die ganzen Konstruktoren ist man natürlich mächtig gebunden (erfordert Disziplin, sonst hagelts Bugs).
2) KEIN zusätzlicher Speicher, dafür eigentlich nur wirklich über Pointer machbar - schön in einem "CounterProxy<T>" versteckt (dessen Destruktor natürlich release() aufruft). create uns release lässt sich per Makro machen "MAKE_COUNTABLE(T)" o.Ä.

Ich würde eher zur 2) tendieren.
Wg. Implementierung: sollte nicht so schwer sein, ich lasse dir den ersten Schritt ;)
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

franzf hat geschrieben:Kern des Problems ist ja, wie in C++ Objekte auf- und abgebaut werden, wie der Reihe nach Konstruktoren- und Destruktoren aufgerufen werden. Da muss also gesteuert werden, wer wann wie zählt. In jedem Falle würde ich den Counter außerhalb der Objekte halten, und immer per ref()/unref() hoch/runterzählen (wir haben ja jetzt keinen Konstruktor/Destruktor mehr).
Ich sehe 2 Möglichkeiten:
1) Zum konstruieren einen public und einen protected-Konstruktor. Der Public macht ein ref() auf den Zähler, der protected nicht. Beim Zerstören gibts nen bool (der muss natürlich bereits in der untersten Klasse vorhanden sein, also am besten eine Basisklasse Countable einführen die den Member hält), der gecheckt wird. Ist der true gibts ein unref() auf den Zähler und der bool wird auf false gesetzt.
2) Geht ohne Basisklasse Countable, dafür muss jede Klasse eine create() und eine destroy() anbieten, in denen ref() und unref() auf den Counter aufgerufen wird.

Vorteile/Nachteile:
1) Großer Nachteil der zusätzliche Speicherbedarf PRO OBJEKT, dafür geht es auch schön mit automatischer Speicherverwaltung ("Stack"). Durch die ganzen Konstruktoren ist man natürlich mächtig gebunden (erfordert Disziplin, sonst hagelts Bugs).
2) KEIN zusätzlicher Speicher, dafür eigentlich nur wirklich über Pointer machbar - schön in einem "CounterProxy<T>" versteckt (dessen Destruktor natürlich release() aufruft). create uns release lässt sich per Makro machen "MAKE_COUNTABLE(T)" o.Ä.

Ich würde eher zur 2) tendieren.
Wg. Implementierung: sollte nicht so schwer sein, ich lasse dir den ersten Schritt ;)
dank fuer deine viele gedanken und ideen.

2) hört sich für mich vernünftiger an. Aber ich bin noch nicht ganz hinter deine idee gekommen. ich zaehle die objekte nicht mehr über die konstr/destr der template klasse counter sondern biete eine methode an ref()/unref() für das hoch/runterzaehlen (bereitgestellt durch die counterklasse). Jede Klasse bietet nun ein create() und destroy() an die ref und unref() aufrufen. Konnte ich dir soweit folgen?

merci!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Aenni hat geschrieben:2) hört sich für mich vernünftiger an. Aber ich bin noch nicht ganz hinter deine idee gekommen. ich zaehle die objekte nicht mehr über die konstr/destr der template klasse counter sondern biete eine methode an ref()/unref() für das hoch/runterzaehlen (bereitgestellt durch die counterklasse). Jede Klasse bietet nun ein create() und destroy() an die ref und unref() aufrufen. Konnte ich dir soweit folgen?
So war mein Gedanke, ja.
Dein counter<T>-Code kann so bleiben, nur dass du den Konstruktor durch ref() und den Destruktor durch unref() ersetzt.
In create() hast du ja Zugriff auf den konkreten Typ, der instantiiert wird, und kannst genau dafür den Zähler per ref() inkrementieren, release umgekehrt.

BTW.: Du brauchst auch noch Code für Zuweisungsoperator und Kopierkonstruktor - so können ja auch neue Objekte entstehen ;) -> HINT: Proxy
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

franzf hat geschrieben: So war mein Gedanke, ja.
Dein counter<T>-Code kann so bleiben, nur dass du den Konstruktor durch ref() und den Destruktor durch unref() ersetzt.
In create() hast du ja Zugriff auf den konkreten Typ, der instantiiert wird, und kannst genau dafür den Zähler per ref() inkrementieren, release umgekehrt.

BTW.: Du brauchst auch noch Code für Zuweisungsoperator und Kopierkonstruktor - so können ja auch neue Objekte entstehen ;) -> HINT: Proxy
die bearbeitete counter klasse:

Code: Alles auswählen

#include <typeinfo>
template <typename T>
class counter {
    static int objects_created;
    static int objects_alive;
public:
    counter()
    {
    }
    void ref(){
        ++objects_created;
        ++objects_alive;
    }
    void unref(){
        --objects_alive;
    }

    static void print_stat() {
        qDebug() << "created "<< objects_created << " alive " << objects_alive ;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
    }
};
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );
test1 und test3 Klasse:

Code: Alles auswählen

template <typename T>
class logger : public counter<T>
{
    std::string last_log_;
public:
    void log(std::string const& msg) {
        last_log_ = msg;
    }
};

class test1 : public logger<test1> {
public:
    test1(){
    }
    ~test1(){
    }
    void create(){
        ref();
    }
    
    void release(){
        unref();
    }
};


class test3 : public test1{
public:

    test3(){
    }
    ~test3(){
    }

    void create(){
        ref();
    }

    void release(){
        unref();
    }
};
den type des objekts koennte ich ja ueber typeinfo bekommen (rtti)... die methode ref und unref bei create und release aufrufen. jetzt weiss ich aber nicht, wie ich in der create und rel methode genau die ref() und unref() aufrufen muss um entsprechend nur den typ der klasse zu erhöhen.
danke fuer anregungen.!
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: crtp & vererbung

Beitrag von franzf »

Ich hab das jetzt einfach mal so gemacht, wie ich es mir vorgestellt hatte.

counter.hpp:

Code: Alles auswählen

#ifndef MY_COUNTER_HEADER
#define MY_COUNTER_HEADER

#include <iostream>

#define COUNTER_MAKE_COUNTABLE(T) \
    public: \
    typedef typename counter<T>::proxy ptr; \
    static ptr create() { return ptr(new T); } \
    private:

template <typename T>
class counter {
    static int objects_created;
    static int objects_alive;
    
    static void ref()
    {
        ++objects_created;
        ++objects_alive;
    }
    
    static void unref() {
        --objects_alive;
    }
        
public:    
    static void print_stat() {
        std::cout << objects_created << " " << objects_alive << std::endl;
    }
    
    class proxy {
        T* obj;
    public:
        proxy(T* o)
         : obj(o)
        {
            counter::ref();
        }
        
        ~proxy() {
            delete obj;
            counter::unref();
        }
        
        T* operator->() {
            return obj;
        }
    };
};


template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );

#endif
test.cpp

Code: Alles auswählen

#include <iostream>

#include "counter.hpp"

class test1 {
    COUNTER_MAKE_COUNTABLE(test1)
public:
    void bamm() {
        std::cout << "BAMM" << std::endl;
    }
};

class test2 : public test1 {
    COUNTER_MAKE_COUNTABLE(test2)
public:
    void bumm() {
        std::cout << "BUMM" << std::endl;
    }
};

int main() {
    for(int i=0; i<10; ++i) {
        test1::ptr t = test1::create();
        t->bamm();
        if(i%2) {
            test2::ptr t2 = test2::create();
            t2->bumm();
        }
    }
    counter<test1>::print_stat();
    counter<test2>::print_stat();
}
Was bleibt?
Überleg dir, wie das mit Kopien von counter::proxy-Objekten sein soll. Ist ein wenig doof... (war es aber mit dem wikipedia-Ansatz auch) Werden bei Kopie(Zuweisung neue Objekte erzeugt? Wie willst du zählen? Was passiert mit den alten Objekten? Jedenfalls ist diese Lösung jetzt äquivalent zu der crtp-Lösung (nur nicht so elegant - du wolltest es ja so :P)
In jedem Fall musst du Konstruktoren + ope= der gezählten Klassen verstecken, nicht dass noch jemand auf dumme Gedanken kommt und Objekte an deinem Zähler vorbei erzeugt :P

BTW. hab ich release wieder über bord geworfen. Da kreuzten sich die Wege mit dem proxy - der kümmert sich ja jetzt ums dereferenzieren + löschen.
Aenni
Beiträge: 79
Registriert: 15. Juli 2010 22:29

Re: crtp & vererbung

Beitrag von Aenni »

wieder einmal herzlichen dank für das Beispielt,

hat nochmal einiges deutlicher gemacht.

werde die fertige idee/klassen posten.
danke.
schönes we
Antworten