Philosophie von C++: Objekte so lokal wie möglich zu halten und so spät wie möglich zu definieren
-
Hacker schrieb:
Ehrlich gesagt seh ich da keinen nennenswerten Unterschied zwischen 2 und 3, außer dass 2 vielleicht sinnlos ist, da - wenn du einen Vector mit Foos hast, dieses Foo natürlich in der for(;;)-Schleife stehen kann.
Jup, dann habe ich in aber das ständige "Objekt erzeugen, Objekt zerstören".
Hacker schrieb:
Wieso aber willst du schon vor Programmablauf festlegen, wie groß die Datei ist?
Das sollte jetzt nur als Beispiel dienen.
Also, worauf ich hinaus wollte ist, wenn ein Objekt nur für eine Schleife gedacht ist, dann sollte man es auch so deklarieren, dass es nur für die Schleife benutzt werden kann.
-
Naja, wenn man beispielsweise in einer Schleife getline() aufruft, sollte man den Zielstring außerhalb definieren, sonst kommt das echt teuer.
-
[Rewind] schrieb:
Ich sehe keinen relevanten Unterschied zwischen Variante 1 und 2.
Ich schon. Bei Variante 1 kann ich Foo f in der kompletten Funktion verwenden. Bei Variante 2 nur für die Schleife. Bezüglich des C++-Paradigmas ist das für mich also ein großer Unterschied. :p
-
Probier's doch einfach aus? (Und schreib am besten mal ein etwas ausführlicheres Beispiel.)
-
Man kann es ja auch anders machen, folgende Schleife fänd ich toll:
for(Foo temp();!DeinStream.eof();vec.push_back(temp)) //Dein Zeug
-
Gugelmoser schrieb:
[Rewind] schrieb:
Ich sehe keinen relevanten Unterschied zwischen Variante 1 und 2.
Ich schon. Bei Variante 1 kann ich Foo f in der kompletten Funktion verwenden. Bei Variante 2 nur für die Schleife. Bezüglich des C++-Paradigmas ist das für mich also ein großer Unterschied. :p
Achso, wenn die Funktion noch weitergeht, dann natürlich ja, aber ich bezog mich nur auf das Beispiel. Sorry, muss den Satz im ersten Beitrag übersehen haben.
-
edit: gelöscht
-
Hacker schrieb:
for(Foo temp;!DeinStream.eof();vec.push_back(temp)) //Dein Zeug
Stimmt, ist aber ja letztendlich dasselbe wie in Variante 2. Du erzeugst temp nur 1x, verarbeitest es in der Schleife und legst es am Ende im Container ab.
Hier habe ich mal noch ein Beipsiel.
#include <string> #include <fstream> #include <vector> using namespace std; class CSNP { private: string snpName; string allele1; string allele2; public: friend istream& operator>>(istream& in, CSNP& csnp) { getline(in,csnp.snpName,' '); getline(in,csnp.allele1,' '); return getline(in,csnp.allele2); } }; int main() { ifstream in("foo.bar"); // min. 1Mio Zeilen. if(!in) return -1; vector<CSNP> objects; for(CSNP csnp; in >> csnp;) // Wäre ja nun Variante 2. { objects.push_back(csnp); } // ab hier geht es dann mit dem Vector weiter, etc. }
-
Edit: wieder gelöscht
-
Hm.. was man machen könnte, wäre einen CSNP Konstruktor zu definieren, der einen std::istream& nimmt und seine Sachen selbst einliest. Damit würde das zu
for (so viele Zeilen wie ich will) objects.push_back(CSNP(in)); // Wirft exception
werden.
Falls du aber einfach "lesen bis zum Ende" möchtest, ist die for-Schleife doch eigentlich gut. Eventuell noch mit Move-Konstruktor für CSNP.
-
Hacker schrieb:
Man kann es ja auch anders machen, folgende Schleife fänd ich toll:
for(Foo temp();!DeinStream.eof();vec.push_back(temp)) //Dein Zeug
Und ist doppelt Falsch. Korrekt:
for(Foo tmp; stream; vec.push_back(tmp)) stream >> tmp;
-
Korrekter:
for(Foo tmp; stream >> tmp; vec.push_back(tmp));
-
Hacker schrieb:
Ehrlich gesagt seh ich da keinen nennenswerten Unterschied zwischen 2 und 3, außer dass 2 vielleicht sinnlos ist.
Wieso aber willst du schon vor Programmablauf festlegen, wie groß die Datei ist?Der Unterschied zwischen 2 und 3 ist, dass bei Variante 3 das Ding pro Schleifendurchlauf neu erstellt wird.
Ich würde das schon als nennenswert bezeichnen.
-
314159265358979 schrieb:
Hacker schrieb:
Man kann es ja auch anders machen, folgende Schleife fänd ich toll:
for(Foo temp();!DeinStream.eof();vec.push_back(temp)) //Dein Zeug
Und ist doppelt Falsch. Korrekt:
for(Foo tmp; stream; vec.push_back(tmp)) stream >> tmp;
upseldidupseldi. Die Klammern hab ich aus versehen reingemacht :p
-
Die Klammern waren ja auch unwichtig.
-
cooky451 schrieb:
Die Klammern waren ja auch unwichtig.
Dann war aber nur eine Sache "Falsch".
-
Gugelmoser schrieb:
Ich persönlich finde Variante 2 ganz schön, da ich es bei Variante 3 absurd finde, in jedem Schleifendurchgang ein Objekt zu erstellen und zu zerstören; das schlägt doch bestimmt auch auf die Performace aus, oder?
Erst messen, dann optimieren. Ist "Verarbeitung von f" einigermaßen teuer, dürfte die Erzeugung und Zerstörung von f ohnehin irrelevant sein. Ist umgekehrt "Verarbeitung von f" relativ billig zu haben, sind Konstruktor und Destruktor wahrscheinlich trivial oder fast trivial. In jedem Falle würde ich direkte Initialisierung vorziehen, wenn das ohne großen Aufwand möglich ist.
for(int i=0; i!=1000000; ++i) { Foo f(/*Verarbeitung*/); vec.push_back(std::move(f)); }
oder gleich
vec.reserve(vec.size()+1000000); for(int i=0; i!=1000000; ++i) { vec.emplace_back(/*params aus Verarbeitung*/); }
Es ist ja nicht einzusehen, wieso Intialisierung+Zerstörung eines Objektes teurer als die Modifizierung (=glorifizierte Zuweisung) eines bestehenden Objektes sein sollten.
Jedenfalls hängt die Wahl der besten Lösung auch vom konkreten Foo ab, eine allgemein gültige Antwort kann es nicht geben.
-
Kann mich camper nur anschließen!
Ein weiterer Kommentar dazu sei, dass man das sehr gut bei Scott Meyers nachlesen kann. Dort sagt er, dass es die Frage ist, ob Konstruktion + Destruktion billiger ist, als einmal den Zuweisungsoperator auszuführen.
-
Danke für die vielen Antworten
-
Ich bin fuer 'Instantiieren' anstatt 'Definieren'.