1500 struct`s direkt in .cpp in vector schreiben
-
brotbernd schrieb:
Besser als Kopieren und exception safer (
) als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Auch mit Return value optimization wird eine Kopie bleiben.Meinst du die Rückgabe eines Smart Pointers?
Warum meinst du, dass eine Kopier bleiben muss?
Laut dem Artikel hier:
http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspxIst das ja genau der Sinn eine solche Kopie zu vermeiden. Exceptionsafe wäre es das ganze dann ja auch.
-
Gustl schrieb:
Und dann habe ich noch eine list als rückgabewert eingebaut [...] vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
Nein, Zeiger bzw. Referenzen solltest du als Verweise auf etwas Bestehendes verwenden. Zum Beispiel auf Objekte in der Liste, die auch nach dem Funktionsaufruf erhalten bleiben. Lokale Objekte werden am Ende der Funktion zerstört, Verweise darauf sind sehr gefährlich.
Aber zeig doch sonst mal die momentane Funktion...

brotbernd schrieb:
exception safer [...] als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Wieso ist ein Smart-Pointer exceptionsicherer als ein Referenzparameter?
-
Nexus schrieb:
brotbernd schrieb:
exception safer [...] als in Referenz schreiben wäre noch die Übergabe in einem smart pointer.
Wieso ist ein Smart-Pointer exceptionsicherer als ein Referenzparameter?
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

-
drakon schrieb:
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

Gerne

ref_list.clear();Ein Algorithmus, der einen Container füllt, darf wohl davon ausgehen, dass dieser zuvor leer ist. Bei der Smart-Pointer-Rückgabe hat man ja vorher auch nichts.
Aber selbst falls der Container bereits Elemente enthält, gibts immer noch den Swap-Trick. Dabei ist ein Vergleich mit der Smart-Pointer-Lösung aber abwegig.
std::list<T> new_list(ref_list); // kann fehlschlagen // hier weiter füllen; kann auch fehlschlagen std::swap(new_list, ref_list);Oder man merkt sich, wieviele Elemente bereits vorher drin waren und löscht den Rest.
-
Nexus schrieb:
drakon schrieb:
Na, mach mal die inserts, die du schon gemacht hast wieder rückgängig.

Gerne

ref_list.clear();Ein Algorithmus, der einen Container füllt, darf wohl davon ausgehen, dass dieser zuvor leer ist. Bei der Smart-Pointer-Rückgabe hat man ja vorher auch nichts.
Hmm. Muss imo nicht unbedingt sein.. Aber das kommt wohl auf das Anwendungsbeispiel an.
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
-
@Nexus
Gut, du hast recht, so pauschal kann man das nicht behaupten. Was wie sicher ist (oder überhaupt sicher sein muss) hängt natürlich von der Funktion und evtl. side effects ab.
Allerdings gefällt es mir nicht so, wenn eine Funktion Annahmen über ihre Benutzung treffen muss. Wenn sich eine falsche Bedienung vermeiden lässt, dann mach ich das auch. Sonst wundert man sich 2Jahre später vielleicht wo die Hälfte seiner Liste hingeschwappt ist.
-
drakon schrieb:
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Klar, die Funktion kann schlechter zu bedienen sein. Dafür ist sie flexibler, weil sie einen nicht zur Heap-Allokation zwingt. Aber wie du sagst, hängt es von den gegebenen Umständen ab.
drakon schrieb:
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
Hat man nur, wenn man die vorherigen Elemente bewahren will (nicht einmal da zwingend). Das war eigentlich nicht die Voraussetzung, weil die Smart-Pointer-Rückgabe diese Funktionalität auch nicht erfüllt.
brotbernd schrieb:
Allerdings gefällt es mir nicht so, wenn eine Funktion Annahmen über ihre Benutzung treffen muss. Wenn sich eine falsche Bedienung vermeiden lässt, dann mach ich das auch.
Preconditions festzulegen ist Aufgabe vieler Funktionen. Gegebenenfalls werden Fehler zur Laufzeit abgefangen (z.B. Exceptions oder je nach Fall Assertions), das muss jedoch nicht sein. Beispiele in C sind nahezu alle Stringverarbeitungsfunktionen, denen du keinen Nullzeiger übergeben darfst. Ein Beispiel in C++ ist die STL, die sich für gewisse Operationen auf Strict Weak Ordering verlässt (du würdest noch viel mehr finden, ist mir nur gerade eingefallen).
Aber mit der falschen Bedienung hast du völlig Recht, das handhabe ich meist auch so. Oft kann man alleine durch die Schnittstelle schon recht viel aussagen. Allerdings ziehe ich irgendwo auch die Grenzen und setze auf gesunden Menschenverstand, schliesslich darf man auch die Dokumentation lesen.

Hier ist das wohl so ein Streitfall. Ich bin zwar der Meinung, dass bei
// Durchsucht ... und speichert die Ergebnisse in results void FindXYZ(std::list<std::string>& results);klar ist, dass
resultslediglich die Suchresultate hält...Naja, mit C++0x und Move-Semantik ist das Thema eh vom Tisch.
-
Nexus schrieb:
drakon schrieb:
Klar geht es immernoch, aber naja. Ich finde es einiges natürlicher da eine Rückgabe zu haben, als irgendwas erzwingen zu wollen, was schlechter zu bedienen ist.
Klar, die Funktion kann schlechter zu bedienen sein. Dafür ist sie flexibler, weil sie einen nicht zur Heap-Allokation zwingt. Aber wie du sagst, hängt es von den gegebenen Umständen ab.
drakon schrieb:
Aber in deinem Beispiel hat man ja auch wieder eine Kopie, was wir ja nicht wollten.
Hat man nur, wenn man die vorherigen Elemente bewahren will (nicht einmal da zwingend). Das war eigentlich nicht die Voraussetzung, weil die Smart-Pointer-Rückgabe diese Funktionalität auch nicht erfüllt.
Also ich bin davon ausgegangen, dass an die Funktion eine Liste (nicht unbedingt leer) übergeben wird. Und dann kann man da nicht einfach auf leer setzen. Wenn man also die Einträge sicher haben will, kommt man auf eine Teilweise Kopier nicht herum.
Wenn man andere Anforderungen hat und kann man das ganze natürlich noch weiter optimieren. Aber ich habe gemeint, dass man die Liste dann einfach per Kopie zurückgibt, also auch ohne eine Heap Allokation und Smart Pointer.Also ganz einfach per Kopie zurückgeben und auf RVA hoffen, wie ich immer noch der Meinung bin, dass es hier ziehen müsste.

-
Nexus schrieb:
Gustl schrieb:
Und dann habe ich noch eine list als rückgabewert eingebaut [...] vielleicht sollte ich da auch einen zeiger als rückgabewert nehmen?
Nein, Zeiger bzw. Referenzen solltest du als Verweise auf etwas Bestehendes verwenden. Zum Beispiel auf Objekte in der Liste, die auch nach dem Funktionsaufruf erhalten bleiben. Lokale Objekte werden am Ende der Funktion zerstört, Verweise darauf sind sehr gefährlich.
Aber zeig doch sonst mal die momentane Funktion...

Hallo,
ja, ich würde dann halt private die liste in der Klasse erstellen und darauf einen pointer zurückgeben.
die momentane Funktion schaut jetzt so aus:
vector<Frage> ASK::search(string &suche_frage) { list_answers.clear(); vector<Frage>::iterator it; for (it = list.begin(), end = list.end(); it != end; ++it) { if((*it).frage.find(suche_frage)!=string::npos) list_answers.push_back(*it); } if(list_answers.empty()) list_answers.push_back(F_nofind); return list_answers; }Frage:
warum soll ich die Schleife so schreiben:for (it = list.begin(), end = list.end(); it != end; ++it)anstatt so:
for(it = list.begin(); it != list.end(); ++it)?
Und warum soll ich den iterator nicht in der klasse selbst definieren, das geschieht ja dann jedes mal bei funktionsaufruf...?Zu der Diskussion SmartPointer kann ich leider nichts sagen, weil ich nicht weiß was das ist!?

Danke
Gruß
GustlEdit:
P.S.: Ich habe die Funktion mit einer Geschwindigkeitsmessfunktion im builder gemessen und bin gerade mal auf 0,0004 sec. gekommen.
-
Ich finde die Funktion gut und recht verständlich, bis auf ein paar Details:
vector<Frage> ASK::search(const string &suche_frage) //const damit der User weiss, dass da sicher nichts verändert wird { list_answers.clear(); vector<Frage>::iterator it; for (it = list.begin(), end = list.end(); it != end; ++it) { if((*it).frage.find(suche_frage)!=string::npos) list_answers.push_back(*it); } if(list_answers.empty()) list_answers.push_back(F_nofind); // das verstehe ich nicht ganz. Wenn die Liste leer ist, heisst das doch, // dass nichts gefunden worden ist, oder? Und wie ist F_nofind definiert? // Könnte es nicht durch einen blöden Zufall genau dieser Wert in die Liste gepusht werden? return list_answers; }Das end machst du dort, damit es nicht in jedem Schleifendurchlauf neu evaluiert werden muss.
Über Smart Pointer findest du sehr viel, wenn du mal danach googelst. Grundsätzlich geht es darum die Ownership von Dynamischen Ressourcen zu verwalten. (Wer ist für das Aufräumen verantwortlich, wann wird kopiert und wann nur auf den gleichen Bereich referenziert usw.)
-
Ok, jetzt verstehe ich das mit dem end. thx
im no_find steht fest als ant1 = "FRAGE NICHT GEFUNDEN"
da er ja woanderst dann die gefunden Antworten ausgibt.
-
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
-
drakon schrieb:
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
find ich allg. nicht.. ich würd ne const-ref zurückgeben(man will ja nix in den antworten rumschreiben und es scheint ein element der klasse zu sein)
und in der fkt würd ich nicht auf empty prüfen sondern halt außerhalb... ist nicht schwerer aber wesentlich übersichtlicher...
bb
-
unskilled schrieb:
drakon schrieb:
Hmm. OK. Kann man so machen. (Auch wenn ichs ein wenig suspekt finde, aber ist wahrscheinlich einfacher, als etwas anderes..)
find ich allg. nicht.. ich würd ne const-ref zurückgeben(man will ja nix in den antworten rumschreiben und es scheint ein element der klasse zu sein)
Das verstehe ich nicht ganz. Was willst du wo zurückgeben?
und in der fkt würd ich nicht auf empty prüfen sondern halt außerhalb... ist nicht schwerer aber wesentlich übersichtlicher...
Darauf wollte ich hinaus und würde es wahrscheinlich auch eher so lösen, aber ich denke mir, dass er da so etwas, wie
output_answers()hat und dann wird halt, wenn nix gefunden wird der Text ausgegeben, ohne auf noch irgendetwas zu prüfen oder so. Ist natürlich nicht sehr sauber, wenn man dann z.B im Code mal prüfen möchte, ob jetzt was gefunden worden ist, oder nicht. Dann muss man das erste Element der Liste nehmen und da mittels String Vergleich etwas überprüfen. Finde ich sehr unschön. Aber naja.. Wenn das andere genau seine Anforderungen erfüllt, warum nicht?
Das ganze schöner zu machen ist nicht direkt schwerer, oder komplizierter, aber sicher ein wenig mehr Aufwand, den man nicht ubedingt haben will, wenn man nur ein paar Anforderungen zu erfüllen hat.
-
vector<Frage> ASK::search(const string &suche_frage) //const damit der User weiss, dass da sicher nichts verändert wird { list_answers.clear(); //... return list_answers; }legt den verdacht nahe, dass list_answers vom typ vector<Frage> ist und ein member der klasse ASK ist...
ich fänds bequem, wenn ich selbst entscheiden könnte, ob der rückgabewert kopiert werden soll oder nicht:
std::copy(ask.search("asd").begin(), ask.search("asd").end(), std::ostream_iterator<Frage>(std::cout, "\r\n"));
(grad ein wenig ein doofes bsp., aber allg. sehe ich keinen grund, etwas, was so und so nie verändert wird, per value zurückzugeben - oder siehst du die notwendigkeit, diesen wert jemals verändern zu wollen?bb
-
Hmm. Ok, jetzt sehe ich, was du meinst.
Ja, wenn das sowieso eine Member der Klasse ist und man da die Antworten reinschreibt, dann liegt es nahe, dass man eine const Referenz auf das interne Objekt zurückgibt.
Eine andere Frage wäre dann aber natürlich, ob es Sinn macht (abgesehen von Optimierungsgedanken) die Liste intern zu halten.