Wenn C++ - Kovarianz-die beste Lösung?
Diese Frage gefragt wurde, hier ein paar Stunden und machte mir klar, dass
Ich habe eigentlich nie verwendet kovariante Rückgabetypen in meinem eigenen code. Für diejenigen, die
nicht sicher, was Kovarianz ist, es ist so dass der Rückgabe-Typ (in der Regel) virtuelle
Funktionen abweichen, vorausgesetzt, die Typen sind Teil der gleichen Vererbung
Hierarchie. Zum Beispiel:
struct A {
virtual ~A();
virtual A * f();
...
};
struct B : public A {
virtual B * f();
...
};
Die verschiedenen Rückgabetypen der beiden f () - Funktionen sind, sagte kovariante. Ältere Versionen von C++ erforderlich, die Rückgabe-Typen gleich sein, also B würde das so Aussehen:
struct B : public A {
virtual A * f();
...
};
So, meine Frage: Hat jemand ein reales Beispiel, wo kovariante Rückgabetypen von virtuellen Funktionen erforderlich sind, oder produzieren eine überlegene Lösung einfach zurückgeben einer base pointer oder Referenz?
- Wenn ich das lese Frage meine Reaktion war genau die gleiche. Was ist das problem - nicht verwenden kovariante Rückgabetypen!
Du musst angemeldet sein, um einen Kommentar abzugeben.
Das kanonische Beispiel ist eine
.clone()
/.copy()
Methode. So können Sie immer tunegal was obj-Typ ist.
Edit: clone-Methode definiert würde in der Object-Basisklasse (wie ist es eigentlich in Java). Also, wenn Klon war nicht kovariante, würden Sie entweder zu werfen, oder würde eingeschränkt werden, um Methoden der Wurzel-Basis-Klasse (die haben nur sehr wenige Methoden, im Vergleich der Klasse des Quell-Objekt der Kopie).
Im Allgemeinen Kovarianz ermöglicht es Ihnen, um auszudrücken, mehr Informationen in der abgeleiteten Klasse als Schnittstelle ist es auch in den base-class interface. Das Verhalten einer abgeleiteten Klasse ist spezifischer als eine Basisklasse, und die Kovarianz drückt (einem Aspekt von) den Unterschied.
Es ist nützlich, wenn Sie im Zusammenhang der Hierarchien von gubbins, in Situationen, in denen manche Kunden wollen eine base-class interface, aber auch andere clients verwenden der abgeleiteten Klasse Schnittstelle. Mit const-correctness weggelassen:
Kunden, die wissen, dass Sie eine Webseite (Browser, vielleicht) wissen, dass es ist sinnvoll zu schauen, query params. Kunden, die mit dem Ressourcen-Basis-Klasse kennen keine solche Sache. Sie wird immer binden der zurückgegebenen
HttpAddress&
zu einemURI&
variable oder temporäre.Wenn Sie vermuten, aber nicht wissen, dass Ihre Ressource-Objekt hat eine HttpAddress, dann können Sie
dynamic_cast
. Aber Kovarianz überlegen ist, "nur zu wissen", und der cast aus dem gleichen Grund, dass die statische Typisierung ist sinnvoll, bei allen.Gibt es alternativen - stick die
getQueryParam
Funktion aufURI
aber machenhasQueryParam
return false für alles (clutters der URI-Schnittstelle). Lassen SieWebPage::getIdentifier
definiert zurückURL&
, tatsächlich wieder eineHttpIdentifier&
, und haben die Anrufer machen eine sinnlosedynamic_cast
(clutters der Aufruf-code und der Dokumentation der Webseite, wo Sie sagen, "der URL zurückgegeben, ist garantiert dynamisch gewirkt zu HttpAddress"). Hinzufügen einesgetHttpIdentifier
FunktionWebPage
(clutters derWebPage
- Schnittstelle). Oder nutzen Sie einfach die Kovarianzmatrix für das, was es bedeutete, zu tun, das ist Ausdruck der Tatsache, dass einWebPage
keineFtpAddress
oder eineMailtoAddress
ist, hat es eineHttpAddress
.Endlich da ist natürlich ein vernünftiges argument, dass Sie sollten nicht über Hierarchien von gubbins, geschweige denn im Zusammenhang der Hierarchien von gubbins. Aber diese Klassen könnte genauso gut sein, Schnittstellen mit rein virtuellen Methoden, so glaube ich nicht, es berührt das die Gültigkeit der Verwendung von Kovarianz.
Ich denke Kovarianz kann nützlich sein, wenn man erklärt, dass factory-Methoden, die eine bestimmte Klasse und nicht die der Basisklasse. Dieser Artikel erklärt dieses Szenario Recht gut, und enthält den folgenden code Beispiel:
Ich finde mich oft mit Kovarianz bei der Arbeit mit vorhandenen code, um loszuwerden, static_casts. In der Regel ist die situation ähnlich:
Dies ermöglicht es mir,
und
statt
und
Nun, wenn er über einen solchen code könnte man argumentieren über die original-design und ob es eine bessere – aber in meiner Erfahrung gibt es oft Einschränkungen, wie z.B. Prioritäten, Kosten/nutzen usw.. die stören mit umfangreichen design-Verschönerung und erlauben nur kleine Schritte wie dieser.
Weiteres Beispiel ist eine konkrete Fabrik, die würde gibt Hinweise auf die konkreten Klassen statt der abstrakten (ich habe es verwendet für die interne Nutzung in der Fabrik, wenn die Fabrik bauen mussten zusammengesetzte Objekte).
dynamic_cast
im VS kann bringen Sie nach unten, um string-Vergleich. (Wenn ich mich Recht erinnere, Das ist, wenn DLLs beteiligt sind.) Dies ist sicherlich Größenordnungen langsamer, dassstatic_cast
.Wird es nützlich sein, in dem Szenario, wo Sie wollen verwenden eine konkrete factory zu konkreten Produkten. Sie wollen immer die meisten spezialisierten Schnittstellen, die allgemein genug...
Den code mit die konkrete Fabrik kann sicher vermuten, dass die Produkte konkrete Produkte, so kann es sicher werden die Erweiterungen bereitgestellt, die von der konkreten Produkt-Klasse mit Bezug auf die abstrakte Klasse. Dies könnte in der Tat als syntaktischer Zucker, aber es ist süss.