2010-06-21 8 views
8

Der Kopierkonstruktor und die Zuweisung des MFC-Stammobjekts CObject sind standardmäßig deaktiviert.Warum CObject-Kopierkonstruktor und -zuweisung deaktivieren

Die Standardklasse Kopie Konstruktor ++ Standard-C hat ein Mitglied-by-Mitglied Kopie. Das Vorhandensein des privaten CObject-Kopierkonstruktors garantiert eine Compilerfehlermeldung, wenn der Kopie -Konstruktor Ihrer Klasse benötigt wird aber nicht verfügbar. Sie müssen daher einen Kopierkonstruktor bereitstellen, wenn Ihre Klasse diese Fähigkeit erfordert.

  • In CObject Quellcode gibt es einen Kommentar:

den Kopierkonstruktor deaktivieren und Zuordnung standardmäßig, so dass Sie Compiler-Fehler statt unerwartetes Verhalten erhalten, wenn Sie durch Objekte übergeben Wert oder Objekte zuweisen.

Meine Frage ist, was ist das Problem mit dem standardmäßigen Bit-für-Bit-Kopierkonstruktor für diese CObject-Klasse? Meiner Meinung nach, wäre es besser, uns den Standard Copykonstruktor zu geben, und wir konnten eine, wenn nötig (tiefe Kopie)

+0

besser entfernen Sie das MFC-Tag, um mehr Benutzer anzuziehen –

+1

Die Antwort wird wahrscheinlich MFC-spezifisch sein, obwohl. – peterchen

+0

Danke, es funktioniert !!! –

Antwort

6

Der Copykonstruktor Standard ist Mitglied-by-Mitglied, nicht bitweise.

Die meisten von CObject abgeleiteten Klassen enthalten - und verwalten direkt - einige Systemressourcen, die keine Referenzzählung oder einen ähnlichen Mechanismus aufweisen. Daher wurde die Auswahl wahrscheinlich unter Berücksichtigung des Standardanwendungsfalls getroffen.

z.B. CGdiObject ist ungefähr:

class CGDIObject : public CObject 
{ 
    HGDIOBJ m_handle; 

    CGDIObject() : m_handle(0) {} 
    // derived classes provide a create, attach etc. 
    ~CGDIObject() { DeleteObject(m_handle); } 
} 

Der Standard Copykonstruktor hier wäre gefährlich sein (was zu Doppel Zerstörung), eine „richtige“ Copykonstruktor Bereitstellung ist überraschend schwierig und teuer.

Ein weiterer Grund kann sein, dass die meisten CObject-abgeleiteten Klassen mutiert werden sollen und somit als Referenz übergeben werden.Eine fehlende Copykonstruktor wird unbeabsichtigte Kopien fangen, die eine Kopie eher mutieren als das Objekt übergeben:

class CMyObject : public CObject 
{ 
    public: 
     AttachFoo(FooHandle foo) { ... } 
     AddBar() { ... } 
}; 

bool InitMySession(CMyObject & obj) 
{ 
    obj.AttachFoo(CreateRawFoo()); 
    obj.AddBar(); 
    obj.AddBar(); 
} 

// ... 
CMyObj mo; 
InitMySession(mo); 

Weglassen der „&“ gibt Ihnen Code, der gut kompiliert, sondern erstellt eine temporäre Kopie, ändert das, und dann fällt es , während mo unverändert bleibt.

Ganz viele APIs folgen diesem Muster, da MFC nicht auf Ausnahmen zur Fehlerbehandlung angewiesen ist (aus historischen Gründen: Nicht alle gezielten Compiler unterstützten sie gut und MFC erfordert eine Menge zusätzlicher Ressourcenbehandlung, die mit Ausnahmen schmerzhaft wird).


Ich glaube nicht, dass diese Entscheidungen gut sind, z.B. abgeleitete Klassen sollten den Standard-Kopierkonstruktor verwenden dürfen, wenn ihre Mitglieder es zulassen (und die meisten Mitglieder sollten dies zulassen).

Die Entscheidung entspricht der "Denkweise" von MFC, und die Anforderungen/Einschränkungen der Zeit MFC wurde erstellt.

+0

guter Punkt, kann die enge Beziehung zwischen MFC und diesen Systemressourcen lassen sie sich entscheiden, dies zu tun. Ich habe deinen zweiten Punkt nicht ganz verstanden, was meinst du, wenn du von mutiert sprichst? –

+0

hinzugefügt mehr Details – peterchen

3

Betrachten Sie die folgenden Angaben enthalten:

class CMyHandle : public CObject 
{ 
    HANDLE hWin32; 
public: 
    CMyHandle() 
    { 
     hWin32 = SomeFunctionThatMakesHandle(); 
    } 
    ~CMyHandle() 
    { 
     CloseHandle(hWin32); 
    } 
}; 

Nun, wenn Sie CMyHandle kopieren, die Der Handle wird zweimal geschlossen, und nachdem eine der CMyHandle Instanzen zerstört wurde, wird die andere Instanz ungültig.

Da eine große Anzahl von MFC-Klassen Handles verwalten, ist es sinnvoll, den Ersteller der Klasse zum expliziten Überschreiben zu zwingen, um Kopierkonstruktoren zu erstellen und Zuweisungsoperatoren zu kopieren.

EDIT: Zum Beispiel:

int main() 
{ 
    CMyHandle h1; //CMyHandle's constructor initializes the handle 
    { 
     CMyHandle h2(h1); //Memberwise copy into h2. In this case, it copies the 
          //handle 
    } //h2 destroyed, closes handle 
    //h1 is now invalid (because the underlying handle was closed)! 
}