Abstrakte Basisklasse - Kopierkonstruktor



  • Hi,

    gewöhnlich deklariere ich Kopierkonstruktor und Zuweisungsoperator als privat um (unbemerktes) Kopieren zu verhindern. Nun habe ich hier eine abstrakte Basisklasse, von der ja (direkt) keine Objekte erzeugt werden können. Wenn ich nun in abgeleiteten Klassen den Kopierkonstruktor und Zuweisungsoperator als privat deklariere, verhindert das aber kein Kopieren oder Zuweisen:

    Abstrakt* a = new Objekt;
    Abstrakt* b = a;
    

    Wie kann ich in diesem Fall ein Kopieren oder Zuweisen verbieten?

    Vielen Dank im Voraus

    Toru



  • gar nicht, denn es wird nur der pointer zugewiesen, bzw. hier initialisiert.
    das objekt bleibt.



  • in deinem beispiel weißt du gar kein objekt einem anderen zu, nur zeiger.
    am ende zeigen beide auf dasselbe objekt. das kannst du nicht verhindern (außer mit enormen aufwand und selbst dann nicht wirklich).

    trotzdem solltest du in der basisklasse zuweisungsoperator und kopierkonstruktor auch privat machen.

    ach und noch etwas: was meinst du mit "unbemerkten" kopieren? oder was hast du dagegen?



  • simon.gysi schrieb:

    gar nicht, denn es wird nur der pointer zugewiesen, bzw. hier initialisiert.
    das objekt bleibt.

    OK, verstehe. Und das heißt, dass nur die abgeleiteten Klassen (im Falle einer abstrakten Basisklasse) einen privaten Kopierkonstruktor und Zuweisungsoperator benötigen?



  • nein. es gibt eine (implizite) konvertierung in die basisklasse (wenn sie eindeutig ist):

    void foo (Base& base, Derived& derived)
    {
       base = derived; //ruft Base::operator= (Base&) auf
    }
    

    und das problem besteht, denn in der regel wirst du ja referenzen/zeiger der basisklasse verwenden, weil du ja polymorph arbeitest.



  • queer_boy schrieb:

    nein. es gibt eine (implizite) konvertierung in die basisklasse (wenn sie eindeutig ist):

    void foo (Base& base, Derived& derived)
    {
       base = derived; //ruft Base::operator= (Base&) auf
    }
    

    und das problem besteht, denn in der regel wirst du ja referenzen/zeiger der basisklasse verwenden, weil du ja polymorph arbeitest.

    OK, hättest du mal einen Beispielcode, wann dieses Kopieren stattfindet, wenn ich in der abstrakten Klasse Kopierkonstruktor und Zuweisungsoperator nicht privat deklariere? Bei

    Abstrakt* a = Objekt;
    Abstrakt* b = a;
    

    besteht dieses Problem ja nicht.



  • das ist auch, wie schon gesagt, keine zuweisung. für eine zuweisung brauchst du zwei (vollständig initialisierte) objekte.

    Abstrakt* a = new Objekt;
    ABstrakt* b = new Objekt;
    
    *a = *b; //Zuweisung über Abstrakt::operator= (Abstract const&)
    
    a = b; //das ist zwar auch eine zuweisung,
    //aber sie weist nur dem einen zeiger den inhalt des anderen zu,
    //also bloß eine andere speicheradresse.
    
    //anmerkung:
    delete a;
    delete b; //Boom, weil a und b auf dasselbe objekt zeigen
    //löschst du es hier zweimal - das geht nicht.
    


  • queer_boy schrieb:

    das ist auch, wie schon gesagt, keine zuweisung. für eine zuweisung brauchst du zwei (vollständig initialisierte) objekte.

    Abstrakt* a = new Objekt;
    ABstrakt* b = new Objekt;
    
    *a = *b; //Zuweisung über Abstrakt::operator= (Abstract const&)
    
    a = b; //das ist zwar auch eine zuweisung,
    //aber sie weist nur dem einen zeiger den inhalt des anderen zu,
    //also bloß eine andere speicheradresse.
    
    //anmerkung:
    delete a;
    delete b; //Boom, weil a und b auf dasselbe objekt zeigen
    //löschst du es hier zweimal - das geht nicht.
    

    Verstehe. Allerdings kann ich (wie von dir erwähnt) _keinen_ privaten Kopierkonstruktor in der abstrakten Basisklasse deklarieren. Ich muss dazu sagen, dass meine Basisklasse rein virtuelle Funktionen enthält, es also natürlich nicht einmal einen Konstruktor gibt (geben kann). Reicht es also dann, den Zuweisungsoperator privat zu deklarieren?



  • Toru schrieb:

    Verstehe. Allerdings kann ich (wie von dir erwähnt) _keinen_ privaten Kopierkonstruktor in der abstrakten Basisklasse deklarieren. Ich muss dazu sagen, dass meine Basisklasse rein virtuelle Funktionen enthält, es also natürlich nicht einmal einen Konstruktor gibt (geben kann).

    Einen Klasse mit rein virtuellen Methoden kann durchaus einen Konstruktor haben (was auch Sinn machen kann), nur das dieser nur indirekt über die Konstruktoren der abgeleiteten Klasse aufgerufen wird. Beispiel das bei mir in Ähnlicher Form im Einsatz ist:

    class Basis
    {
        private:
            static unsigned long oidCounter;
            unsigned long oid;
    
            Basis(Basis const &);
            Basis& operator=(Basis const &);
        public:
            // <-- Diverse rein virtuelle Methoden
    
            unsigned long GetOID();
            Basis();
            virtual ~Basis();
    };
    
    // in der cpp [Auszugsweise]
    Basis()
    :   oid(++oidCounter)
    {}
    

    Die Klasse kann zwar nicht direkt angelegt werden, der Konstruktor wird aber dennoch verwendet (und zwar wird der Konstruktor durch den Konstruktor der abgeleiteten Klasse für den Basis-Anteil aufgerufen - Mit Initialsierungslisten in der abgeleiteten Klasse kann man auch abweichende Konstruktoren neben den Standardkonstruktor verwenden).

    cu André



  • asc schrieb:

    Toru schrieb:

    Verstehe. Allerdings kann ich (wie von dir erwähnt) _keinen_ privaten Kopierkonstruktor in der abstrakten Basisklasse deklarieren. Ich muss dazu sagen, dass meine Basisklasse rein virtuelle Funktionen enthält, es also natürlich nicht einmal einen Konstruktor gibt (geben kann).

    Einen Klasse mit rein virtuellen Methoden kann durchaus einen Konstruktor haben (was auch Sinn machen kann), nur das dieser nur indirekt über die Konstruktoren der abgeleiteten Klasse aufgerufen wird. Beispiel das bei mir in Ähnlicher Form im Einsatz ist:

    class Basis
    {
        private:
            static unsigned long oidCounter;
            unsigned long oid;
    
            Basis(Basis const &);
            Basis& operator=(Basis const &);
        public:
            // <-- Diverse rein virtuelle Methoden
    
            unsigned long GetOID();
            Basis();
            virtual ~Basis();
    };
    
    // in der cpp [Auszugsweise]
    Basis()
    :   oid(++oidCounter)
    {}
    

    Die Klasse kann zwar nicht direkt angelegt werden, der Konstruktor wird aber dennoch verwendet (und zwar wird der Konstruktor durch den Konstruktor der abgeleiteten Klasse für den Basis-Anteil aufgerufen - Mit Initialsierungslisten in der abgeleiteten Klasse kann man auch abweichende Konstruktoren neben den Standardkonstruktor verwenden).

    cu André

    Verstehe. Danke!



  • Hi Toru,

    ich denke, Du verwechselst hier einfach etwas. Wenn Du jedenfalls das Kopieren von Zeigern verhindern möchtest, hat das mit Kopierkonstruktoren auch nichts zu tun.
    Bestenfalls kannst Du die Objekterzeugung kapseln und den Direktzugriff auf die Objekte unterbinden ... damit bekommt der Anwender einfach gar keinen Zeiger auf ein Objekt mehr in die Hand und damit hast Du das "Zeigerkopieren" unterbunden.

    Aber damit schneidest Du Deine Objekte von allem ab, was "Kopiersemantik" benötigt ... z.B. die STL-Container wie vector, Arrays, ....
    Ich weiß nicht, ob's das wert ist.

    Beispiel:

    #include <vector>
    #include <iostream>
    using namespace std;
    
    class BFactory;
    
    class B {
       B(B const&) {}
       B() {}
       B* operator&() { return this; }
       friend class BFactory;
    public:
       void f() { cout << "B::f()\n"; }
    };
    
    class BFactory {
       vector<B*> bs;
       BFactory(BFactory const&) {}
       int no;
    public:
       BFactory() : no(0) {}
       B& createOne() { bs.push_back(new B); return *bs.back(); }
    
       ~BFactory() { for(vector<B*>::iterator it = bs.begin(); it != bs.end(); ++it) delete *it; }
    };
    
    int main() {
       BFactory bf;
       B& b = bf.createOne();
       b.f();
    
       B* bc = &b; // error: 'B* B::operator&()' is private
    
       return 0;
    }
    

    Gruß,

    Simon2.


Anmelden zum Antworten