Wann rein virtuelle Funktionen?
-
Simon2 schrieb:
Konrad Rudolph schrieb:
...
(EDIT: Meyers nennt es NVI: Non-virtual method idiom.)
...Ähhh, "NVI" bedeutet aber "non virtual interface"-Idiom
… Ja, meinte ich; Schreibfehler …
und Herb Sutter plädiert dort dafür, dass das public interface keine virtuellen Funktionen enthalten sollte.
Das ist doch eine ziemlich andere Aussage als
Konrad Rudolph schrieb:
...Methoden, die virtuell sind, sollten idR pur virtuell sein. Alle anderen Methoden sollten idR nicht virtuell sein....
Na ja. Erst einmal ist das Ziel dasselbe, nämlich die Schnittstelle sauberzuhalten und dem Ableiter eine möglichst kleine, und dafür gezielte, Angriffsfläche zu bieten.
Und dann ist (IMHO) Herb Sutters Ansatz eine Extremposition von NVI, und der von mir referierte Standpunkt eben eine abgeschwächte Form.
-
Konrad Rudolph schrieb:
...
… Ja, meinte ich; Schreibfehler …Darauf wollte ich gar nicht hinaus (das wäre selbst mir zu kleinlich
), sondern darauf, dass es bei der NVI IMHO um etwas ganz Anderes geht.
Mal Andersherum: Wie würdest Du denn Dein Statement
Konrad Rudolph schrieb:
...Methoden, die virtuell sind, sollten idR pur virtuell sein. Alle anderen Methoden sollten idR nicht virtuell sein....
praktisch umsetzen ?
Vielleicht komme ich so zu einem Verständnis, was Du mit dieser Aussage meinst.
So wie ich es verstehe, wäre es gar nicht umsetzbar - und meine Erfahrung mit Dir in diesem Forum legt mir nahe, dass Du das nicht meinst.Gruß,
Simon2.
-
Simon2 schrieb:
Konrad Rudolph schrieb:
...
… Ja, meinte ich; Schreibfehler …Mal Andersherum: Wie würdest Du denn Dein Statement
Konrad Rudolph schrieb:
...Methoden, die virtuell sind, sollten idR pur virtuell sein. Alle anderen Methoden sollten idR nicht virtuell sein....
praktisch umsetzen ?
Vielleicht komme ich so zu einem Verständnis, was Du mit dieser Aussage meinst.
So wie ich es verstehe, wäre es gar nicht umsetzbarVielleicht bewertest Du diese Aussage auch einfach als zu absolut. Sie ist ja nur eine Richtlinie. Dass man damit allein nicht auskommt, ist klar.
Außerdem schrieb ich ja bereits, dass das nicht ganz korrekt ist, weil in vielen Fällen (siehe Template Method) einfach nicht erforderlich ist, eine Funktionalität bereitzustellen; in diesem Fällen sollte man dann aber eben leere Default-Implementierungen bereitstellen.
Damit sollte man dann ganz gut über die Runden kommen. Worauf ich hinaus wollte, war eigentlich nur, dass es sehr selten sinnvoll ist, einerseits Default-Implementierungen anzubieten, diese aber andererseits überschreibbar zu machen. Die meisten solcher Fälle lassen sich mit Schablonenmethoden sauberer ansteuern.
-
Rein virtuelle Funktionen sind sowas von unnütz...
oder ich zu blöd um den Sinn zu kapieren...Wenn ich in der Basisklasse rein virtuelle Funktionen hab (sprich kein
Definition) wozu brauch ich die Vorherdeklaration, wenn ich die Methode
doch in der Unterklasse sowieso deklarieren und definieren muss ?Wozu dient das?
hat jemand ein Beispiel?
-
Quellcode schrieb:
Rein virtuelle Funktionen ...
Wozu dient das?
hat jemand ein Beispiel?Programmierer X stellt ein Konzept zur Verfügung:
struct A { virtual void machs() const = 0; }; void machsMitallenA(vector<A*> const& v) { for(vector<A*>::const_iterator it=v.begin(); it != v.end(); ++it) { it->machs(); } }
Programmierer ABC nutzt dieses Konzept:
struct B : A { void machs() const { cout << "mache B-Zeug\n"; } }; struct C : A { void machs() const { cout << "mache C-Zeug\n"; } }; int main() { vector<A*> v; v.push_back(new B); v.push_back(new B); v.push_back(new C); v.push_back(new A); // krach !!! machsMitallenA(v); return; }
Die rein virtuelle Funktion A::machs() gibt also allen Nutzern von "Nachkommen von A" die Zusage, dass alle diese Gemeinsamkeit haben, "machs()" zu können.
Gleichzeitig verpflichtet es jeden, der von A ableiten (und instantiieren) will, machs() auch umzusetzen.
Es ist also eine Schnittstellenzusage und damit genauso hilfreich wie das Verwenden von Typen (statt void*) und const bei Funktionsdeklarationen.
Gruß,
Simon2.
-
Konrad Rudolph schrieb:
Damit sollte man dann ganz gut über die Runden kommen. Worauf ich hinaus wollte, war eigentlich nur, dass es sehr selten sinnvoll ist, einerseits Default-Implementierungen anzubieten, diese aber andererseits überschreibbar zu machen. Die meisten solcher Fälle lassen sich mit Schablonenmethoden sauberer ansteuern.
Einer von uns beiden hat Template Methode nicht verstanden...
Template Method in meinen Augen ist eine Möglichkeit Teile einer Funktion oder eines Algorithmus redefinieren zulassen. In meinen Augen kommt da nirgendwo pure virtual vor - es ist nichts anderes als ein praktisches interface über eine virtuelle Schnittstelle gepackt -> daher ja auch Sutters NVI Idiom. In C++ Coding Standards kommt in dem NVI Abschnitt zB auch nie das wort pure virtual vor...
-
Quellcode schrieb:
Rein virtuelle Funktionen sind sowas von unnütz...
oder ich zu blöd um den Sinn zu kapieren...Wenn ich in der Basisklasse rein virtuelle Funktionen hab (sprich kein
Definition) wozu brauch ich die Vorherdeklaration, wenn ich die Methode
doch in der Unterklasse sowieso deklarieren und definieren muss ?Wozu dient das?
Ganz einfach dazu, dass die Basisklasse die abgeleitete Klasse zwingt, eine Definition bereitzustellen. Das ist doch sehr sinnvoll: Die Basisklasse bestimmt, *was* zu tun ist, die abgeleitete Klasse bestimmt, *wie* es getan wird.
hat jemand ein Beispiel?
struct ReadStream { virtual void open(string const& resource) = 0; virtual string read_all() = 0; }; struct FileReadStream : ReadStream { void open(string const& resource) { ifs.open(resource.c_str()); } string read_all() { return string(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()); } private: fstream ifs; }; struct HttpReadStream : ReadStream { void open(string const& resource) { hres = OpenHttpResource(resource.c_str()); } string read_all() { stringstream ret; char buffer[80]; int read; while ((read = HttpReadStream(hres, buffer, 80) != 0) { if (read !=80) buffer[read] = '\0'; ret << buffer; } return ret.str(); } private: HANDLE hres; }; string load_whole_file(ReadStream& stream, string const& resource) { stream.open(resource); return stream.read_all(); } int main() { ReadStream* s; string path; cout << "Enter path name: " << flush; cin >> path; if (path.find("http://") == 0) s = new HttpReadStream(); else s = new FileReadStream(); string result = load_whole_file(s, path); }
-
Danke für die Antworten, jetzt bin ich schlauer
-
man seid ihr theoretisch. Ob pure virtual, virtual oder nicht ergibt sich doch aus der programmlogik.
-
powhu schrieb:
man seid ihr theoretisch. Ob pure virtual, virtual oder nicht ergibt sich doch aus der programmlogik.
und woraus ergibt sich die Programmlogik ?
Gruß,
Simon2.
-
Simon2 schrieb:
powhu schrieb:
man seid ihr theoretisch. Ob pure virtual, virtual oder nicht ergibt sich doch aus der programmlogik.
und woraus ergibt sich die Programmlogik ?
Programmlogik ist vielleicht der falsche Begriff. Ich hab noch nie nach irgendwelchen Regeln Methodenattribute vergeben. Wenn ich ein Interface brauche, dann mach ich halt alles pure virtual. Brauch ich ne abstrakte Klasse, dann nur nen Teil pure virtual. Und wenn man Methoden überschreiben können soll, dann halt virtuel. Das ergibt sich halt wie die Parameter und Rückgabewerte einer Methode, da schau ich nicht ob das "Template Method" oder sonst was ist.