BigThree Problem



  • dev_MAddoX schrieb:

    1. nutzt du das template nicht, sondern codest den typ ABC fest rein.

    Hast natuerlich recht. Ich meine natuerlich

    T* matrix_[2];
    

    dev_MAddoX schrieb:

    2. geht das?: matrix_[2] ????

    warum sollte es nicht?

    dev_MAddoX schrieb:

    Zuweisungoperator:

    void oparator=(Container<T> ziel, Container<> quelle)
    

    Was soll das denn?
    Ich will den operator= doch nicht als friend function implementieren.

    dev_MAddoX schrieb:

    ziel.getMatrix() = quelle.getMAtrix();
    

    Dann wuerde ich nur die Zeiger kopieren und nicht die Objekte.
    Aber genau das ist mein Problem, die Matrix zu kopieren.
    Ich haette gerne sowas gemacht:

    Container operator=(const Container<T>& c)
        {
            if(this == &c) return;
    
            for(int i=0; i<2; ++i)
                delete matrix_[i];
    
            for(int i=0; i<2; ++i)
                if(c.matrix[i] == NULL)
                    this->matrix_[i] = NULL;            
                else
                    this->matrix_[i] = new T(c.matrix_[i]);
            return *this;
        }
    

    Haette glaube ich auch hingehaun(haette es??), waere die Klasse nicht generisch.
    Weil ich sie aber mit dem Typ ABC parametrisiert habe(der ja abstrakt ist) funktioniert new T(c.matrix_[i]); schon mal gar nicht.
    Hat jemand schonmal sowas gehabt?

    thx in advance



  • Wie kommst du eigentlich darauf dass du dich darum kümmern müsstest ?

    Der typ sollte einen vernünftigen assignment-op und copyctor implementieren und dann kannst die benutzen.

    const Container &operator=(const Container<T>& c)
        {
            if(this == &c) return;
    
            *matrix_[0]=c.get_matrix1();  //op= des enthaltenen typen
            *matrix_[1]=c.get_matrix2(); 
    
            return *this;
        }
    

    Für den copy-ctor genau so.



  • Argh...

    musst natürlich noch dereferenzieren.

    const Container &operator=(const Container<T>& c)
        {
            if(this == &c) return;
    
            *matrix_[0]=*c.get_matrix1();  //op= des enthaltenen typen
            *matrix_[1]=*c.get_matrix2();
    
            return *this;
        }
    

    überseh ich jetzt was wesentliches ?



  • KurzeFrage schrieb:

    Argh...

    musst natürlich noch dereferenzieren.

    const Container &operator=(const Container<T>& c)
        {
            if(this == &c) return;
           
            *matrix_[0]=*c.get_matrix1();  //op= des enthaltenen typen
            *matrix_[1]=*c.get_matrix2();
    
            return *this;
        }
    

    überseh ich jetzt was wesentliches ?

    IMHO schon.
    Was passiert mit den vorherigen Elementen der Matrix?

    *matrix_[0]=*c.get_matrix1();  //op= des enthaltenen typen
    

    Was ist nun mit dem Element, das vorher an *matrix_[0] stand?
    Du hast es nicht deleted, also hast du nun ein schoenes memory leak.



  • Nein hat er nicht, die bekommen neue werte.

    Er kopiert keinen speicherbereich sondern , weisst die klasse(ABC in diesem fall) an die werte zuzuweisen. Was ja auch richtig ist weil nur sie weiss wie man das am besten macht.



  • Einverstanden.
    Ich habe aber unter meinen Elementen auch NULL Pointer.
    Ich behandele diese wie folgt:

    Container operator=(const Container<T>& c)
        {
            if(this != &c)
            {   
                for(int i=0; i<2; ++i)
                    if(c.matrix_[i] == NULL) 
                    {
                        delete this->matrix_[i];
                        this->matrix_[i] = NULL;
                    }            
                    else
                    {
                        //Wer sagt mir, dass *this->matrix_[i] != NULL ist?
                        *this->matrix_[i] = *c.matrix_[i];
                    }           
            }
            return *this;
        }
    

    Falls mir ein NULL Pointer beim kopieren von einem Containerobjekt A unterkommt, delete ich das Element und setze es auf NULL. Wenn nun auf A erneut der operator= aufgerufen wird, dann schlaegt folgende Anweisung fehl:

    //Wer sagt mir, dass *this->matrix_[i] != NULL ist?
                        *this->matrix_[i] = *c.matrix_[i];
    


  • Du könntest einfach testen ob es NULL ist. Da du ja sowieso das element löschen würdest in diesem fall, machst du einfach nichts mit diesem element oder wäre das nicht im sinne des erfinders ?

    //EDIT NOCHMAL ... machst du doch nicht, bleibt dir in diesem fall also nichts übrig als nochmal zu testen, dann würdest du eben ein neues element anlegen müssen, dafür könntest zum beispiel den copy-ctor der klasse benutzen.

    if(matrix[i]==NULL)
         matrix[i]=new T(*c.matrix[i]);
    


  • Und schon waeren wir beim Ausgangsproblem.

    Weil ich sie aber mit dem Typ ABC parametrisiert habe(der ja abstrakt ist) funktioniert new T(c.matrix_[i]); schon mal gar nicht.



  • Dann musst du einen weg finden dass keine NULL-pointer auftauchen. Das ist ja normalerweise auch eine ausnahme und ich würde da eine exception werfen. Möglich wäre doch zum beispiel einen leeren typ zu definieren oder sowas. Sodass du imma mit validen objekten arbeitest, die eben nur nicht notwendigerweise imma auch definierte werte haben.



  • Dann musst du einen weg finden dass keine NULL-pointer auftauchen.

    Finde ich keine befriedigende Loesung 😞
    Die NULL Pointer brauche ich, um zu erkennen, ob das Feld meiner Matrix leer ist.
    Und immer ein Test mit instanceof find ich auch nicht gut, wenn ich einen leeren Typ anlege.

    Das new Problem(matrix[i]=new T(*c.matrix[i]);) wuerde sowieso spaetestens im CopyCtor erneut auftreten, weil ich da auf jeden Fall Speicher anfordern muss.

    Container(const Container<T>& c) 
    { 
        for(int i=0; i<2; ++i) 
            this->matrix_[i] = new T(c.matrix_[i]);
    }
    

    Angenommen, ich liesse mich auf deinen Vorschlag ein, dann gaebe es aber noch folgendes Problem:

    *this->matrix_[i] = *c.matrix_[i];
    

    Diese Technik called immer den operator= von T, also von ABC.
    Um dieses Problem zu loesen muesste ich den operator= in ABC virtual definieren.
    Das zoege nach sich, dass alle Klassen in meinem Vererbungsgraphen zuweisungskompatible zueinander sind.
    Sowas waere also Problemlos moeglich:

    class ABC
    {
    public:
        virtual ABC& operator=(const ABC&){}
    };
    
    class KlasseA_dieNichtsMit_B_zuTunHat : public ABC
    {
    public:
        KlasseA_dieNichtsMit_B_zuTunHat operator=(const KlasseA_dieNichtsMit_B_zuTunHat& A)
        {
            ABC::operator=(A);
        }
    };
    
    class KlasseB_dieNichtsMit_A_zuTunHat : public ABC
    {
    public:
        KlasseB_dieNichtsMit_A_zuTunHat operator=(const KlasseB_dieNichtsMit_A_zuTunHat& B)
        {
            ABC::operator=(B);
        }
    };
    
    void sample(ABC& a, ABC& b)
    { a=b;   //unsinnig, aber leider erlaubt }
    
    int main()
    {
        KlasseA_dieNichtsMit_B_zuTunHat A;
        KlasseA_dieNichtsMit_B_zuTunHat B;
        sample(A,B);
    }
    

    Durch die Namen angedeutet, kann man erkennen, dass KlasseA_dieNichtsMit_B_zuTunHat
    und KlasseA_dieNichtsMit_B_zuTunHat nichts miteinander zu tun haben, ausser eine gemeinsame ABC. Trotzdem koennte ich sie einander zuweisen.
    In den Klassen meiner Vererbungshierarchie existiert eigentlich kein solcher Isomorphismus, dass ich so etwas wollte. 😋



  • Hmm ich versteh das problem ehrlich gesagt nicht.

    Im copy-ctor kann man locker mit dem blosen assignment-op der enthaltenen klasse arbeiten. Die erzeugung der objekte wäre dann auch wieder nicht deine aufgaben. Da wäre dann ein

    container.insert(new derived());
    

    angebracht.

    Und naja ein container soll ja wenn er was enthält auch was enthalten was sinnvoll ist, darum geht es doch gerade. Ich versteh auch nicht warum die beiden abgeleiteten klassen, wenn sie nichts miteinander zu tun haben, dann von der selben basisklasse erben. In diesem moment haben sie was gemeinsam, nämlich die basis. Wenn du das nicht willst dann musst du deinem container eben sagen dass er nicht container der basisklasse ist sondern jeweils einen container für die abgeleiteten klassen instanzieren.

    class A{
    
    };
    
    class B : public A{
    
    };
    
    class C : public A{
    
    };
    
    container<A*> container_A_;
    container<B*> container_B_;
    container<C*> container_C_;
    
    container_A_.insert(new B());
    container_A_.insert(new C());
    
    container_B_.insert(new B());
    container_C_.insert(new C());
    
    //conatainer_B_=container_C_  schlaegt dann fehl
    

    Oder überseh ich wieder was ?



  • prolog schrieb:

    Hmm ich versteh das problem ehrlich gesagt nicht.

    Im copy-ctor kann man locker mit dem blosen assignment-op der enthaltenen klasse arbeiten.

    ohne vorher speicher zu allozieren? das wuerde mich sehr wundern, nachdem man mir hier versicherte genau das tun zu muessen.

    Die erzeugung der objekte wäre dann auch wieder nicht deine aufgaben. Da wäre dann ein

    container.insert(new derived());
    

    Sicher, das wuerde das Problem loesen, wenn ich wuesste, dass ich wirklich gerade ein derived kopiere und nicht ein anderes von ABC abgeleitetes Objekt derived2.
    Leider weiss ich aber nicht, welche Instanz ich von meinen 12 von ABC abgeleiteten Klassen gerade kopiere. D.h. es muesste ein Typtest her und der ist ganz sicher nicht im Sinne des Erfinders.

    class ABC;
    class A : public ABC { /* blablabla */ };
    class B : public ABC { /* */};
    class C : public ABC...
    class D....
    ...
    
    class Container //enthaelt ABC Objekte
    Container(const Container& c)
    {
        for(int i=0; i<1000; ++i)            
            if(c.matrix_[i] instanceof A)
                this->matrix_[i] = new A(c.matrix_[i]);
            else if(c.matrix_[i] instanceof B)
                this->matrix_[i] = new B(c.matrix_[i]);
            else if(c.matrix_[i] instanceof C)
                this->matrix_[i] = new C(c.matrix_[i]);
            else if(c.matrix_[i] instanceof D)
                this->matrix_[i] = new D(c.matrix_[i]);
            //und so weiter...
    
            //ich kann keine Typen nachtraeglich hinzufuegen, ohne eine weitere Verzweigung hinzuzufuegen.
            //gaaanz schlecht!!
    }
    };
    

    Ich versteh auch nicht warum die beiden abgeleiteten klassen, wenn sie nichts miteinander zu tun haben, dann von der selben basisklasse erben. In diesem moment haben sie was gemeinsam, nämlich die basis.

    Nur die Basis. Das heisst noch lange nicht, dass sie den selbem abstrakten Status haben, der fuer einen virtuellen assingnmentOp notwendig waere. Nur weil mehrere Klassen von derselben ABC erben, will ich doch noch lange nicht, dass man sie alle einander gegenseitig zuweisen kann

    class Shape
    {
    protected:
        int x,y;
    };
    
    class Square : public Shape { };
    class Circle : public Shape { };
    
    void sample(Shape& a, Shape& b)
    { a=b; }
    
    int main()
    {
        Square a;
        Circle b;
        sample(a,b); // vollkommen unsinnig!!!
    }
    

    Vielleicht verstehe ich dich auch nicht, dann koenntest du dich ja an dem Codefragment probieren, dass ich anfangs gepostet hatte. Waere gespannt auf deine Loesung.



  • Gehört TGGC eigentlich auch zu den Big Three?



  • naaa schrieb:

    Gehört TGGC eigentlich auch zu den Big Three?

    Nein, dafür ist er zu schlecht.



  • ich denke mal HumeSikkins und volkard gehören auf jeden Fall dazu. Aber wer noch? 🙄



  • och leute....



  • Ok, also wenn du weisst dass du nur abgeleitete klassen verwalten musst dann brauchst du keine templates. Du arbeitest eben mit den basisklassenzeigern.

    Dass man keine instanz einer abstracten klass erzeugen kann liegt in der natur der c++-dinge, da wirst du nichts dran drehen können. Was du willst ist offensichtlich eine deep-copy-semantic beim op= und copy-ctor. Gut dann musst
    du aber dafür sorgen dass du nie mit null-pointern zu tun hast. Weil sonst immer das problem mit der instanzierung der basis auftritt. Der container ist nicht für allocierung bzw. deallocierung verantwortlich. Wenn der container leer ist enthält er kein objekt, wenn er die größe 1 hat dann enthält er eben ein definiertes objekt. Es ist aufgabe des clients dafür zu sorgen dass da ordentliche werte vorhanden sind um damit arbeiten zu können wie ers erwartet.

    Kannst du mal sagen warum du diesen weg überhaupt gehst mit dem eigenen container ? Ich meine einen groben umriss des kontexts geben, dass man mal schaun kann ob das nicht auch anders geht ?

    Ich würde wahrscheinlich boost::array <basis*> verwenden. Aber es kann ja sein dass das für dich nich ausreicht weil du noch irgendwas besonderes machen willst. Aber vom jetzigen standpunkt aus verschliesst sich mir irgendwie der sinn des ganzen.

    Ansonsten kannst du auch einfach ne shallow-copy der zeiger machen, musst halt nur drauf achten dass die zeiger nicht ausversehen durch einen andren container zerstört werden. Deswegen sollte der container auch die elemente nicht im dtor oder ähnliches freigeben. Dafür ist auch der client verantwortlich.

    Sry nochmal, das muss frustrierend für dich sein aber probiers doch nochmal genau zu schildern bitte.



  • big three schrieb:

    ich denke mal HumeSikkins und volkard gehören auf jeden Fall dazu. Aber wer noch? 🙄

    TGGC 👍



  • Ok, also wenn du weisst dass du nur abgeleitete klassen verwalten musst dann brauchst du keine templates. Du arbeitest eben mit den basisklassenzeigern.

    Gut, dann werde ich meine Klasse ungenerisch machen.

    Dass man keine instanz einer abstracten klass erzeugen kann liegt in der natur der c++-dinge, da wirst du nichts dran drehen können. Was du willst ist offensichtlich eine deep-copy-semantic beim op= und copy-ctor.

    Ja 🙂

    Gut dann musst
    du aber dafür sorgen dass du nie mit null-pointern zu tun hast. Weil sonst immer das problem mit der instanzierung der basis auftritt

    Ich brauchte halt einen Wert, der mir signalisiert, dass in einem Feld kein ABC Objekt vorhanden ist. Der NULL Pointer bot sich IMHO dafuer an.

    Der container ist nicht für allocierung bzw. deallocierung verantwortlich.

    Wie jetzt? Wer dann? Ich wuesste nicht, wie ich beim CopyCtor um eine Allokation herumkommen koennte.

    Kannst du mal sagen warum du diesen weg überhaupt gehst mit dem eigenen container ? Ich meine einen groben umriss des kontexts geben, dass man mal schaun kann ob das nicht auch anders geht ?

    Ich brauchte einige spezielle member functions, die kein std container bereitstellt. Meine Container Klasse ist auch nur ganz kurz und eigentlich 😉 recht simpel.

    Es geht mir im Moment nicht so sehr darum, dass ich ja viel einfacher eine bereits fertige Containerklasse verwenden koennte.
    Das eigentliche Problem liegt darin: Ich habe eine Klasse, die ein Attribut vom Typ Zeiger auf ABC hat. Ich habe mich jetzt gefragt, wie ich den CopyCtor und den AssignmentOp zu implementieren habe, um das Attribut zu kopieren, ohne seinen genauen Typ zu wissen(weil ich ja 12 Unterklassen habe) und dabei noch die NULL Pointer angemessen behandeln zu koennen.
    std::vector etc koennen das doch auch! 😞 Wie machen die das denn?
    Mir kommt das Problemm so alltaeglich vor!



  • Hi,

    wenn ich micht nicht irre passiert bei vectoren von zeigern auch keine tiefe copie. AFAIK werden da nur zeiger kopiert. Deswegen kann der das. Aber das ist ja nicht was du willst. Weil eine veränderung eines elements im einen container eine änderung desselben elements im andren container nach sich ziehen würde.

    Ich hab allerdings jetzt auch nicht nachgesehen, also wenn das nicht stimmen sollte möge man mich korrigieren.

    Aber eins scheint mir sicher, du kannst keine instanz der basis erzeugen um dann evtl. danach die ableitete klasse tief reinzukopieren. Das musst du irgendwie umgehen.

    Man könnte ja zum beispiel sowas überlegen.

    class base{
         public:
            explicit base(const bool c=false):concrete_(c){}
            virtual ~base(){}
            virtual void func()=0;
            void concrete(const bool &n){concrete_=n;}
            const bool &concrete() const {return conctrete_;}
        private:
            bool concrete_;
    };
    
    class derived : public base{
          public:
              derived(const int v,const int v2) : base(true),v1_(v),v2_(v2) {}
              void func(){}
          private:
              int v1_;
              int v2_;
    };
    

    Dann könntest du mit zeigern und tiefen copien in deinem container arbeiten. Und wenn du die werte brauchst müsstest du vorher testen ob concrete true ist oder nicht und entsprechend im einen fall von unsinn ausgehen und im andren von sinnvollen werten.


Anmelden zum Antworten