2016-07-25 36 views
0

Ich habe eine einfache Container-Klasse, die auf eine abstrakte Klasse zeigt und ich habe Funktionen, um den Zeiger in der Container-Klasse zu erhalten/setzen. Genauer gesagt, sieht die Klasse wie folgt:C++ Das abgeleitete Objekt der abstrakten Klasse in eine andere Klasse einfügen, ohne die Zeiger zu hängen?

class Container 
{ 
    Abstract* thing; 
public: 
    void set(Abstract &obj) 
    { 
     thing = &obj; //danger of dangling pointer 
    } 

    Abstract* get() 
    { 
     return thing; 
    } 
}; 

Abstract eine abstrakte Klasse ist. Wie bereits zu sehen ist, besteht die Gefahr eines baumelnden Zeigers. Ich weiß, dass ich eine Kopie des Objekts (neu) machen und darauf zeigen könnte. Aber ich kann keine Instanz einer abstrakten Klasse erstellen. Welche Lösungen gibt es dafür?


Die folgenden sind nur weitere Informationen:

Klassendefinitionen

class Abstract 
{ 
public: 
    virtual void something() = 0; 
}; 

class Base : public Abstract 
{ 
    int a; 
public: 
    Base() {} 
    Base(int a) : a(a){} 
    virtual void something() 
    { 
     cout << "Base" << endl; 
    } 
}; 

class Derived : public Base 
{ 
    int b; 
public: 
    Derived() {} 
    Derived(int a, int b) : Base(a), b(b){} 
    virtual void something() 
    { 
     cout << "Derived" << endl; 
    } 
}; 

Einfache Tests

void setBase(Container &toSet) 
{ 
    Base base(15); 
    toSet.set(base); 
} 

void setDerived(Container &toSet) 
{ 
    Derived derived(10, 30); 
    toSet.set(derived); 
} 

int main() 
{ 
    Container co; 

    Base base(15); 
    Derived derived(10, 30); 

    Base *basePtr; 
    Derived *derivedPtr; 

    //This is fine 
    co.set(base); 
    basePtr = static_cast<Base *>(co.get()); 
    basePtr->something(); 

    //This is fine 
    co.set(derived); 
    derivedPtr = static_cast<Derived *>(co.get()); 
    derivedPtr->something(); 

    //Reset 
    basePtr = nullptr; 
    derivedPtr = nullptr; 

    //Dangling pointer! 
    setBase(co); 
    basePtr = static_cast<Base *>(co.get()); 
    basePtr->something(); 

    //Dangling pointer! 
    setDerived(co); 
    derivedPtr = static_cast<Derived *>(co.get()); 
    derivedPtr->something(); 

    return 0; 
} 
+0

Möchten Sie den Grund des Downvotes teilen? – silentwf

+2

Mögliches Duplikat von [Was ist ein intelligenter Zeiger und wann sollte ich einen verwenden?] (Http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use) -one) – LogicStuff

+0

@silentwf Die Downvote-Gründe werden im Tooltip geteilt, wenn Sie die DV-Schaltfläche mit dem Mauszeiger bewegen. –

Antwort

6

Was Sie tun müssen, ist Ihr mich zu definieren mory Besitz konkret.

Container::set nimmt eine Instanz von Abstract Bezug genommen wird, die in der Regel keine Eigentumsübertragung bedeutet:

void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it 

Dann wird die Last der Löschung ist auf Sie nicht.

Container::get gibt einen Zeiger das Eigentum impliziert, was darauf hinweist, dass jemand, set Anrufe sollten nicht das übergebene Objekt ungültig machen.

Abstract* get(){...} 

Dies könnte problematisch sein, wie Sie gesagt haben.

Sie haben ein paar Optionen

  • Encode diese Speicherbesitz-Semantik innerhalb Container mit der richtigen Dokumentation (Code by contract)
  • Verwenden Sie ein Smart-Pointer wie std::shared_ptr

Im ersteren Fall, ob es funktioniert oder nicht, hängt davon ab, dass der Benutzer Ihre API liest und versteht und sich dann gut damit verhält. Im letzteren Fall besitzt das Zeigerobjekt selbst und löscht den zugewiesenen Speicher, wenn die letzte Instanz den Gültigkeitsbereich verlässt.

+0

Weil ich Dummheit hasse wie 'delete myContainer.get();' kann ich dich in 'Abstract & get() {...} 'sprechen? – user4581301

+0

@ user4581301: Warum würden Sie 'myContainer.get()' löschen? Es löscht sich selbst und der manuelle Aufruf von 'delete' führt zu einem Segmentierungsfehler. – AndyG

+0

Yup. Es ist leichtsinnig, aber Sie können es tun. Das Problem wird wahrscheinlich nicht mit eklatanten und unmittelbaren Fehler sein, aber falsches Handling, was später wie ein roher Zeiger ein paar Dutzend Zeilen Code aussieht. Der Hinweis erhöht die Schwierigkeit, versehentlich in die absichtlich schlecht benehmende Kategorie zu schrauben. – user4581301

1

Wenn Sie befürchten, dass das Objekt anderswo freigegeben wird, was zu einem ungeeigneten Zeiger führt, könnten Sie Smart-Pointer verwenden.

Boost Smart Pointer würde Ihnen den Service der Buchhaltung bieten und helfen, einen solchen Fall zu vermeiden.

einige Informationen hier: smart pointers (boost) explained

1

Dies ist, was std::unique_ptr ist für:

class Container 
{ 
    std::unique_ptr<Abstract> thing; 
public: 
    void set(std::unique_ptr<Abstract> obj) 
    { 
     thing = obj; 
    } 

    Abstract* get() 
    { 
     return thing.get(); 
    } 
}; 

Nun ist die Abstract Objekt automatisch die, wenn sie von Container und wird gereinigt „Besitz“ ist Conatiner ist zerstört.

Wenn Sie einen Zeiger verwenden möchten, der länger leben kann, oder von mehreren Containern gemeinsam genutzt werden kann, verwenden Sie stattdessen std::shared_ptr.