Oft ist es eine gute Idee, eine abstrakte Basisklasse zu haben, um die Schnittstelle des Objekts zu isolieren.
Das Problem ist, dass die Kopierkonstruktion IMHO in C ++ standardmäßig ziemlich kaputt ist und standardmäßig Kopierkonstruktoren generiert werden.
Was sind die Fallstricke, wenn Sie eine abstrakte Basisklasse und Rohzeiger in abgeleiteten Klassen haben?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
Und jetzt deaktivieren Sie die Kopierkonstruktion für die gesamte Hierarchie sauber? Kopierkonstruktion als privat deklarieren in IAbstract
?
Gibt es Dreierregeln mit abstrakten Basisklassen?
c++
abstract-class
Codierer
quelle
quelle
Antworten:
Die Kopierkonstruktion für eine abstrakte Klasse sollte in den meisten Fällen privat gemacht werden, ebenso wie der Zuweisungsoperator.
Abstrakte Klassen sind per Definition ein polymorpher Typ. Sie wissen also nicht, wie viel Speicher Ihre Instanz verwendet, und können ihn daher nicht sicher kopieren oder zuweisen. In der Praxis riskieren Sie das Schneiden: /programming/274626/what-is-the-slicing-problem-in-c
Der polymorphe Typ in C ++ darf nicht durch den Wert manipuliert werden. Sie bearbeiten sie durch Referenz oder durch Zeiger (oder einen beliebigen intelligenten Zeiger).
Dies ist der Grund, warum Java Objekte nur durch Referenz manipulierbar gemacht hat und warum C # und D die Trennung zwischen Klassen und Strukturen aufweisen (die erste ist polymorph und der Referenztyp, die zweite ist nicht polymorph und der Werttyp).
quelle
Sie können es natürlich auch nur geschützt und leer machen, damit abgeleitete Klassen auswählen können. Im Allgemeinen ist Ihr Code jedoch sowieso verboten, da er nicht instanziiert werden kann
IAbstract
- weil er eine rein virtuelle Funktion hat. Daher ist dies im Allgemeinen kein Problem. Ihre Schnittstellenklassen können nicht instanziiert und daher niemals kopiert werden, und Ihre abgeleiteten Klassen können das Kopieren nach Belieben verbieten oder fortsetzen.quelle
operator=(const Derived2&)
in deklarierenDerived1
.Indem Sie ctor und die Zuweisung privat machen (oder sie in C ++ 11 als = delete deklarieren), deaktivieren Sie die Kopie.
Der Punkt hier ist, wo Sie das tun müssen. Um bei Ihrem Code zu bleiben, ist IAbstract kein Problem. (Beachten Sie, dass Sie bei dem, was Sie getan haben, das
*a1
IAbstract
Unterobjekt a2 zuweisen und dabei keinen Verweis auf verlierenDerived
. Die Wertzuweisung ist nicht polymorph.)Das Problem kommt mit
Derived::theproblem
. Das Kopieren eines Abgeleiteten in ein anderes kann tatsächlich die*theproblem
Daten gemeinsam nutzen, die möglicherweise nicht für die gemeinsame Nutzung ausgelegt sind (es gibt zwei Instanzen, diedelete theproblem
ihren Destruktor aufrufen können).Wenn dies der Fall ist
Derived
, muss dies nicht kopierbar und nicht zuweisbar sein. Wenn Sie die Kopie privat machen, kann sie natürlich auch nicht kopiert werdenIAbstract
, da die Standardkopie sieDerived
benötigtDerived
. Wenn Sie jedoch Ihre eigenen definieren,Derived::Derived(const Derived&)
ohneIAbtract
copy aufzurufen , können Sie sie dennoch kopieren.Das Problem befindet sich in Abgeleitet, und die Lösung muss in Abgeleitet bleiben: Wenn es sich um ein Nur-Dynamik-Objekt handeln muss, auf das nur über Zeiger oder Referenzen zugegriffen werden kann, muss es Abgeleitet sein
Im Wesentlichen ist es
theproblem
Sache des Designers der Klasse Derived (der wissen sollte, wie Derived funktioniert und wie verwaltet wird), zu entscheiden, was mit Zuweisung und Kopieren geschehen soll.quelle