Ich untersuche derzeit das Zusammenspiel zwischen polymorphen Typen und Zuweisungsoperationen. Mein Hauptanliegen ist, ob jemand versuchen könnte, den Wert einer Basisklasse einem Objekt einer abgeleiteten Klasse zuzuweisen, was zu Problemen führen würde.Erkennung der Zuordnung der Basisklasse zum Referenzieren auf die abgeleitete Klasse
Von this answer habe ich gelernt, dass der Zuweisungsoperator der Basisklasse immer durch den implizit definierten Zuweisungsoperator der abgeleiteten Klasse verborgen ist. Für die Zuweisung zu einer einfachen Variablen führen falsche Typen zu Compilerfehlern. Allerdings ist dies nicht der Fall, wenn die Zuordnung über eine Referenz auftritt:
class A { public: int a; };
class B : public A { public: int b; };
int main() {
A a; a.a = 1;
B b; b.a = 2; b.b = 3;
// b = a; // good: won't compile
A& c = b;
c = a; // bad: inconcistent assignment
return b.a*10 + b.b; // returns 13
}
Diese Form der Zuordnung wahrscheinlich Objektzustand führen würde inconcistent, jedoch gibt es keine Compiler-Warnung und der Code sieht nicht böse auf mich zuerst Blick.
Gibt es ein etabliertes Idiom, um solche Probleme zu erkennen?
Ich denke, ich kann nur auf die Laufzeit-Erkennung hoffen, werfen eine Ausnahme, wenn ich eine solche ungültige Zuordnung finden. Der beste Ansatz, den ich mir gerade vorstellen kann, ist ein benutzerdefinierter Zuweisungsoperator in der Basisklasse, der Laufzeittypinformationen verwendet, um sicherzustellen, dass this
tatsächlich ein Zeiger auf eine Instanz der Basis und nicht auf eine abgeleitete Klasse ist Führt eine manuelle Kopie von Mitglied zu Mitglied durch. Dies klingt nach viel Aufwand und beeinträchtigt die Lesbarkeit des Codes erheblich. Gibt es etwas leichter?
Bearbeiten: Da die Anwendbarkeit einiger Ansätze davon abhängt, was ich tun möchte, hier sind einige Details. Ich habe zwei mathematische Konzepte, sagen ring und field. Jedes Feld ist ein Ring, aber nicht umgekehrt. Es gibt mehrere Implementierungen für jede, und sie teilen gemeinsame Basisklassen, nämlich AbstractRing
und , wobei letztere von der ersteren abgeleitet sind. Jetzt versuche ich eine leicht zu schreibende by-reference Semantik basierend auf std::shared_ptr
zu implementieren. So enthält meine Klasse Ring
eine std::shared_ptr<AbstractRing>
, die ihre Implementierung enthält, und eine Reihe von Methoden, die an diese weiterleiten. Ich möchte Field
als Erben von Ring
schreiben, also muss ich diese Methoden nicht wiederholen. Die Methoden, die für ein Feld spezifisch sind, würden den Zeiger einfach auf AbstractField
werfen, und ich würde das gerne statisch machen. Ich kann sicherstellen, dass der Zeiger tatsächlich ein AbstractField
bei der Konstruktion ist, aber ich mache mir Sorgen, dass jemand eine Ring
zu einer Ring&
zuweisen wird, die eigentlich eine Field
ist, womit meine angenommene Invariante über den enthaltenen geteilten Zeiger gebrochen wird.
Ist hier nicht das eigentliche Problem, dass Sie eine nicht abstrakte Basisklasse haben? –
Persönlich deaktiviere ich Kopierkonstruktor und Zuweisungsoperatoren für polymorphe Typen. Vererbungsbasis-Polymorphismus spielt wirklich nicht gut mit Werttypen. –
@OliCharlesworth: Ich sehe nicht wie. Denken Sie z.B. Ein Toggle-Button, der von einer Schaltfläche abgeleitet ist, zeigt Situationen, in denen die Basisklasse instanziierbar sein sollte und das Problem in der realen Welt auftreten könnte. Daher würde ich keinem Ansatz "Alle Grundlagen müssen abstrakt sein" folgen. Wenn das nicht Ihr Ziel ist, dann erläutern Sie bitte, wie abstrakte Basisklassen in meiner Situation helfen können. – MvG