Doppeldeutigkeit nach mehrfachvererbung



  • Sone schrieb:

    Das ist das klassische Beispiel des Diamond-Problems.

    Wie volkard schon sagte, der Diamond of Death ists nicht. Das wäre er wenn die beiden Oberklassen nochmal von ein- und derselben Basisklasse erben würden und die angepeilte Variable in der obersten Basisklasse definiert wäre.

    Mh, was mich mal interessieren würde. Wie erbt man von einem Singleton sinnvoll?
    Wenn wir mal hiervon ausgehen.

    class SingletonBase
    {
    	SingletonBase(){}
    	SingletonBase(SingletonBase const&); // = delete
    	SingletonBase& operator=(SingletonBase); // = delete
    
    public:
    	static SingletonBase& getInstance()
    	{
    		static SingletonBase s;
    		return s;
    	}
    };
    

    Dann ist ganz schnell klar, dass static + Inheritance nicht harmonieren, sprich vererben geht nicht.

    Auf diese folgende Weise schafft man zwar eine Vererbungshierarchy, aber es hat eigentlich nur Nachteile.

    class SingletonBase
    {
    protected:
    	SingletonBase(){}
    
    private:
    	SingletonBase(SingletonBase const&); // = delete
    	SingletonBase& operator=(SingletonBase); // = delete
    
    public:
    	static SingletonBase& getInstance()
    	{
    		static SingletonBase * s = cond ? new SingletonBase : new SingletonDerive; // mal abgesehen von der manuellen Speicherinkontinenz
    
    		return s;
    	}
    
    	virtual ~SingletonBase(){}
    };
    
    class SingletonDerive : public SingletonBase
    {
    };
    

    Wie macht man das in der Praxis?
    (Oben die 2 Beispiel sind ohne viel Denken jetzt von mir kurz runtergeschrieben)



  • volkard schrieb:

    Sone & Tobiking2 schrieb:

    Diamond-Problem

    Echt? Nö, eigentlich nicht.

    Skym0sh0 schrieb:

    Wie volkard schon sagte, der Diamond of Death ists nicht. Das wäre er wenn die beiden Oberklassen nochmal von ein- und derselben Basisklasse erben würden und die angepeilte Variable in der obersten Basisklasse definiert wäre.

    Was versteht Ihr denn anderes als das Diamond-Problem unter der Aussage vom TE?

    gamer804 schrieb:

    Im original code habe ich zwei klassen die beide die klasse CDefaultThreadingModel erben.
    nun will ich eine dritte klasse erstellen, welche sich von den beiden ersten klassen ableitet, aber habe natürlich das problem, dass die member von CDefaultThreadingModel doppelt existieren



  • Das stimmt schon so.

    Aber das gegebene Beispiel vom TE spiegelt nicht das Diamond Problem wieder.



  • Mh, was mich mal interessieren würde. Wie erbt man von einem Singleton sinnvoll?
    Wenn wir mal hiervon ausgehen.

    also so sieht mein grundgerüst aus:
    (eine abgewandelte version von dem singleton von andrei alexandrescu)

    // CSingleton
    	// -> this class is a very variable implementation of the Singleton design pattern descriped by the GoF
    	//    "Ensure a class only has one instance, and provide a global point of access to it."
    	template<
    		class T,
    		template<class> class TLifetime = DELTA_CORE_SINGLETON_LIFETIME_DEFAULT,
    		template<class> class TAlloc = DELTA_CORE_ALLOCATOR_DEFAULT,
    		template<class,class> class TThreadingModel = DELTA_CORE_THREAD_MODEL_CLASSLEVEL,
    		class TMutex = DELTA_CORE_THREAD_MUTEX_DEFAULT
    	>
    	class CSingleton{
    	public:
    		typedef T obj_type; // type of the singleton object
    
    		static obj_type& getInstance();
    
        private:
            // Helpers
            static void createSingleton();
            static void destroySingleton();
    
            // Protection
            CSingleton();
    		CSingleton(const CSingleton&);
    		CSingleton& operator=(const CSingleton&);
    		~CSingleton();
    
            // Data
            typedef typename TThreadingModel<T*,TMutex>::volatile_type ptr_instance_type;
            static ptr_instance_type m_instance;
            static bool m_destroyed;
    	};
    
    class c : public CSingleton<c>{
    
    };
    

    damit solltest du schnellstmöglich aufhören

    Ich glaube ich sollte echt mal drauf achten, dass ich singleton viel seltener benutze. ich glaube ich sollte anders denken und versuchen umwege zu nehmen.
    ...ach ich hab ja noch zeit das alles nen bisschen zu üben (bin ja erst 16)

    Wieso kommt es sonst zu Speicherfehlern?

    die klasse ist noch nicht ganz fertig, aber jetzt wo du es sagst... warum eigentlich 😃 ich glaube ich hab da irgentwo einen anderen fehler, ich gucke nochmal nachher nach.

    Das ist das klassische Beispiel des Diamond-Problems.

    scheint genau das zu sein was ich brauche, ich versuche dann mal das in meinen code zu implementieren.

    ich mag singletons aber irwie lieber, weil ich dann mehr kontrolle habe wann was erstellt und gelöscht wird und ich kann im destructor alles aufräumen

    Tatsächlich? Wie genau kontrollierst du denn das bei einem Singleton? Und wieso kannst du nur bei einem Singleton "im Destruktor aufräumen"!?

    nein kann ich nicht nur bei singletons, das war auf den umweg bezogen alles als globale funktionen und globale variablen zu schreiben und dann hätte ich für die globalen variablen keinen destructor.



  • CSchmeiss Cdoch Cdie Tganzen TPraefixe Tweg. CPfui.



  • CSchmeiss Cdoch Cdie Tganzen TPraefixe Tweg. CPfui.
    

    😃 hab ich mir so angewöhnt, dann weiß man immer direkt was eingabe und was membervariablen sind, man erkennt direkt klassen und datenstrukturen und templateparameter und und und...
    ich finde es angenehmer einen solchen code im nachhinein zu ändern also ne 😃 ich lasse das nicht...



  • gamer8o4 schrieb:

    hab ich mir so angewöhnt

    UN ist eine der schlimmsten Angewohnheiten von C++-Programmierern.

    Siehe hier.



  • UN ist eine der schlimmsten Angewohnheiten von C++-Programmierern.
    

    ungarische notation heißt soweit ich weiß, dass man bei allen variablen den typ abgekürzt davor schreibt.
    Ich schreibe ja nur C T m_ p_ das war es und ich finde das hat wenig mit UN zu tun...
    mir ist klar, dass es unnötig solche sachen wie das folgende zu schreiben, aber das macht ja auch niemand

    const unsigned long long* m_kull_ptr_MyVar = nullptr;
    


  • gamer8o4 schrieb:

    Ich schreibe ja nur C T m_ p_ das war es und ich finde das hat wenig mit UN zu tun...

    Lies mal meinen Link ausführlich.

    Falls das nicht reicht, kein Problem. Ich habe auch einen separaten Post nur den C-Präfixen gewidmet 😉



  • gamer8o4 schrieb:

    damit solltest du schnellstmöglich aufhören

    Ich glaube ich sollte echt mal drauf achten, dass ich singleton viel seltener benutze. ich glaube ich sollte anders denken und versuchen umwege zu nehmen.
    ...ach ich hab ja noch zeit das alles nen bisschen zu üben (bin ja erst 16)

    Ähm. Nicht das Vermeiden der Singletons ist der Umweg, sondern das Benutzen. Das klingt gerade, als würde man Dir eine Säge wegnehmen und Du würdest jammern, daß man mit dem Schraubenzieher noch schlechter Blumen gießen kann.

    gamer8o4 schrieb:

    nein kann ich nicht nur bei singletons, das war auf den umweg bezogen alles als globale funktionen und globale variablen zu schreiben und dann hätte ich für die globalen variablen keinen destructor.

    Da ist doch schon das Problem. Du verwendest überall globale Variablen. Nochmal: Da ist das Problem. Daß du die globalen Variablen dann als Singletons tarnst, ist keine wirklich tragfähige Lösung.



  • ...



  • ----------------------------------------------------------------------------------------------------------------------------------------------------------------
    Regel Nr.1 für die Anwendung von Singletons: Niemals von Singletons ableiten !!!
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------

    Egal was man auch immer für eine Klasse, für was auch immer für einen Anwendungsfall hat.
    Die Bedingung nur eine Instanz davon zu erlauben ist kontextabhängig und hat in allen Fällen nichts mit der Klasse selbst zu tun.

    class c
    {
     // aus bequemlichkeit innerhalb der klasse realisiert
     c* instance() 
     { 
        return singleton<c>::instance(); 
     }
    }
    

    Das Singleton Interface kann auch total außerhalb der Klasse sein.

    // freie funktion
    c* single_instance()
    { 
      return singleton<c>::instance();
    }
    

    Merke: Siehst du eine Klasse, die von einem Singleton erbt, hat es ein Anfänger programmiert.



  • Merke: Siehst du eine Klasse, die von einem Singleton erbt, hat es ein Anfänger programmiert.

    Wenn das stimmen sollte, gibt es auch Anfänger mit mehreren Jahren Berufserfahrung. 🤡



  • Merke: Siehst du eine Klasse, die von einem Singleton erbt, hat es ein Anfänger programmiert.

    ähäm, wenn du meinst Andrei Alexandrescu wäre ein anfänger... dann gibt es aber sehr wenig profis 😃

    Aber deine idee mit dem auslagern ist super. dann kann ich auf die ganzen lösungsvorschläge von diesem diamond-problem ablassen 💡
    allerdings hab ich noch eine frage, wenn ich das singleton so verwende wie du es als beispiel angegeben hast kann ich die klasse einfach kopieren?
    das heißt ich müsste dann kopierkonstrukoren und operatoren noch private machen.
    Naja, ich folge mal diesem ansatz, der scheint mir sinnvoll 🙂



  • Andrei Alexandrescu ist sowieso ein Verräter, da er zu D gewechselt ist.



  • Andrei Alexandrescu ist sowieso ein Verräter, da er zu D gewechselt ist.

    😃
    er ist nicht nur dazu gewechselt, er entwickelt die sprache mit.
    Das heißt aber noch lange nicht, das er C++ nicht drauf hat, ich finde seine bücher genial.



  • gamer8o4 schrieb:

    ähäm, wenn du meinst Andrei Alexandrescu wäre ein anfänger... dann gibt es aber sehr wenig profis 😃

    Alexandrescus Leistung an der Stelle war, dass er gezeigt hat, was alles mit templates möglich ist. Aber das wichtigste was man aus dem Kapitel hätte mitnehmen müssen ist: Singletons sind absoluter scheissendreck der an allen möglichen Stellen mit dem Sprachstandard kollidiert und mehr Probleme schafft, als er löst. Das ganze Kapitel mit all den Veränderungen ist doch ein gigantisches "tut das nicht!". Warum tust du es trotzdem?



  • gamer8o4 schrieb:

    Das heißt aber noch lange nicht, das er C++ nicht drauf hat, ich finde seine bücher genial.

    Sie lesen sich unglaublich nett.
    Muss aber zugeben, daß ich aus seinen Büchern nur genommen habe, daß ich seitdem wesentlich besser templierern kann. Die Extreme in den Büchern aber sind nicht sachdienlich in der Praxis, soweit ich sehe. Das wertet dir Bücher nicht ab. Hätte er bescheiden templiert, wäre ich noch auf der Suche nach Grenzen. Tag für Tag. Er hat mir 50 Jahre zu forschen erspart in beiden Richtungen, was ich mehr und was ich weniger templieren sollte.
    👍 👍 👍 Andrei Alexandrescu 👍 👍 👍



  • Also, das ich mich noch einmal rechtfertigen darf bitte:

    Im original code habe ich zwei klassen die beide die klasse
    CDefaultThreadingModel erben.
    nun will ich eine dritte klasse erstellen, welche sich von den beiden ersten klassen ableitet, aber habe natürlich das problem, dass die member von CDefaultThreadingModel doppelt existieren

    Ich habe den Code gar nicht angeschaut. Nach diesem Satz schien mir klar, wo das Problem lag.
    Das der TE das falsch beschrieben hatte kam mir doch nicht in den Sinn...



  • gamer8o4 schrieb:

    also so sieht mein grundgerüst aus:
    (eine abgewandelte version von dem singleton von andrei alexandrescu)

    // CSingleton
    	// -> this class is a very variable implementation of the Singleton design pattern descriped by the GoF
    	//    "Ensure a class only has one instance, and provide a global point of access to it."
    	template<
    		class T,
    		template<class> class TLifetime = DELTA_CORE_SINGLETON_LIFETIME_DEFAULT,
    		template<class> class TAlloc = DELTA_CORE_ALLOCATOR_DEFAULT,
    		template<class,class> class TThreadingModel = DELTA_CORE_THREAD_MODEL_CLASSLEVEL,
    		class TMutex = DELTA_CORE_THREAD_MUTEX_DEFAULT
    	>
    	class CSingleton{
    	public:
    		typedef T obj_type; // type of the singleton object
    
    		static obj_type& getInstance();
    
        private:
            // Helpers
            static void createSingleton();
            static void destroySingleton();
            
            // Protection
            CSingleton();
    		CSingleton(const CSingleton&);
    		CSingleton& operator=(const CSingleton&);
    		~CSingleton();
            
            // Data
            typedef typename TThreadingModel<T*,TMutex>::volatile_type ptr_instance_type;
            static ptr_instance_type m_instance;
            static bool m_destroyed;
    	};
    
    class c : public CSingleton<c>{
    
    };
    

    Also

    template<typename T,typename TM>
    class Sigleton{
       static T& getInstande();
       TM threadingModel;
    };
    class Drucker:public Singleton<Drucker,ThreadingModelSingleThreaded>{
    };
    class Monitor:public Singleton<Monitor,MultiThreadedDFBSD>{
    };
    class AB:public Drucker,public Monitor{
    };
    

    Und immer noch ist da keine Raute des Todes.

    Natürlich geht

    AB ab;
    ab.getInstance();//wollte ich Drucker oder Monitor haben? 
    ab.getInstance().threadingModel;//wollte ich vom Drucker oder vom Monitor dem sein ThreadinModel haben?
    

    nicht. Das ist aber aus C++-Sicht nicht rautig (voll pöse), sondern bloß ambig (leicht unbequem). Aber es ist inhaltlich nicht auflösbar als Folge eines Designfehlers, fürchte ich.

    class AB:public Drucker,public Monitor{
       void lock(){
          this->Drucker::lock();
          this->Monitor::lock();
       }
    };
    

    Nicht gut.

    Also doch die Raute des Todes bauen. Klar.

    template<typename T,typename TM>
    class Sigleton:publich TM{
       static T& getInstande();
    };
    templateytypename TM>
    class Drucker:virtual public Singleton<Drucker,TM>{
    };
    templateytypename TM>
    class Monitor:virtual public Singleton<Monitor,TM>{
    };
    template<typename TM>
    class AB:public Drucker<TM>,public Monitor<TM>{
    };
    

    Klar. Helau. 🤡 🤡 🤡
    War eigentlich ganz einfach. Man muß sich nur immer von einem kleinen Designfehler in den nächst größeren treiben lassen. Tag für Tag. Das macht Spaß.


Anmelden zum Antworten