Welche der beiden Schreibweisen aus dem Titel ist besser?
Beide funktionieren, ich frage mich allerdings, ob es gute Gründe für
die eine oder andere Variante gibt.
Und dann habe ich noch eine kleine Frage: Warum funktioniert die zweite
Variante? Ich konnte keinen passenden Operator finden:
Zitat von https://doc.qt.io/qt-5/qstring.html:
1
QString & operator=(const QByteArray &ba)
2
QString & operator=(QString &&other)
3
QString & operator=(const QString &other)
Dafür bräuchte man doch einen Operator mit Parameter char* bzw. char[],
oder irre ich?
Dirk K. schrieb:> Ändere mal den Code, so dass innerhalb der> Schleife ein Random-String zugewiesen wird.
Ich fürchte, dann teste ich primär den Zufallsgenerator.
Deine "Meinung" deckt sich mit meiner, deswegen hatte ich erwartet dass
der zweite Test eindeutig länger dauert. Aber das ist nicht der Fall.
Bei mehreren Wiederholungen ist er meistens sogar schneller.
Da findet nirgends eine Zuweisung statt. Man könnte operator= löschen
und der Code würde trotzdem noch funktionieren.
Das das 6 Posts nach Start des Threads immer noch niemand richtig
beantwortet hat zeigt auf wie sinnvoll Guidelines sind die überall (wos
geht) Uniform-Initialization vorschlagen...
/edit
Noch lustiger ist dass die falsche Antwort einen Upvote erhalten hat. :)
Stefan ⛄ F. schrieb:> Dirk K. schrieb:>> Ändere mal den Code, so dass innerhalb der>> Schleife ein Random-String zugewiesen wird.>> Ich fürchte, dann teste ich primär den Zufallsgenerator.>> Deine "Meinung" deckt sich mit meiner, deswegen hatte ich erwartet dass> der zweite Test eindeutig länger dauert. Aber das ist nicht der Fall.
Der 2.Fall (=) braucht minimalste länger beim compilieren, da in der AST
zuerst das "assign" drinsteht und beim optimieren wieder rausfliegt. Ich
vermute du benutzt GCC oder Clang, beide sind schlau genug für dieses
"schwere" optimierungsproblem.
Und selbst wenn nicht und ohne dir zu nahe treten zu wollen: das wird
nicht das größtes Performance-Problem in deiner Qt-Anwendung sein. ;-)
Zudem
> Bei mehreren Wiederholungen ist er meistens sogar schneller.
Auch da kommen Compiler auf lustige Ideen, wie "Schleifen ausrollen".
Das geht auch für nur laufzeitbekannte Anzahl Durchläufen. "Duff's
Device" ist auch bei Optimierern bekannt.
Vincent H. schrieb:> Da findet nirgends eine Zuweisung statt. Man könnte operator= löschen> und der Code würde trotzdem noch funktionieren.
Wie kommt dann bei
QString s="Hallo"
die Zeichenkette in s hinein, wenn das weder eine Zuweisung noch ein
Aufruf des operator=(char*) ist?
Welcher mir unbekannter Mechanismus steckt dahinter?
Von jemandem, der C++-Tutorials schreiben will, hätte ich diese Frage
jetzt nicht erwartet. Das in beiden Fällen der Konstruktor QString(const
char*) und nicht operator= aufgerufen wird, ist eigentlich Basis
C++-Wissen.
Oliver
Oliver S. schrieb:> Das in beiden Fällen der Konstruktor QString(const> char*) und nicht operator= aufgerufen wird, ist eigentlich Basis> C++-Wissen.
Wusste ich noch nicht. Man lernt nie aus.
Offenbar werden Konstruktoren nicht nur zum Initialisieren sondern auch
zur Typ-Konvertierung verwendet.
Ist das neu?
Ich hatte C++ in den 90er Jahren gelernt und seit dem fast nur in Java
und C programmiert.
Stefan ⛄ F. schrieb:> Wie kommt dann bei>> QString s="Hallo">> die Zeichenkette in s hinein, wenn das weder eine Zuweisung noch ein> Aufruf des operator=(char*) ist?
Nimm dir doch einfach den Urgroßvater namens C (hier mal als C99, wegen
des const-Schlüsselworts):
1
constinti=42;
Auch wenn das keine wirkliche Konstante ist sondern nur eine
unveränderbare Variable: durch das "const" kannst du dem "i" niemals
etwas zuweisen. Damit ist also klar, dass das Ganze keine Zuweisiung
ist. Es ist syntaktisch eine Definition der Variablen inklusive
Initialisierung.
Auch wenn du hier nun lesen konntest, dass man bei C++ diese Form nicht
mehr präferiert, vorhanden ist sie durch die Verwandtschaft mit C (und
entsprechendem Code in freier Wildbahn, der das so macht) halt immer
noch.
ps: Natürlich konnte man auch schon immer implizite Typkonvertierungen
in einer Initialisierung machen. Auch das ist gültiger C-Code:
1
constinti=3.1415926;
auch, wenn er natürlich sinnlos ist (und mittlerweile die Compiler
darüber warnen).
Damit sollte sich auch erklären, warum das normale C++-String-Literal in
einem C++-Initializer auftauchen darf; der Compiler sucht dann den am
besten passenden Konstruktor dafür.
Stefan ⛄ F. schrieb:> Offenbar werden Konstruktoren nicht nur zum Initialisieren sondern auch> zur Typ-Konvertierung verwendet.
Ja. Das nennt sich auch konvertierender Konstruktor und ist nichts neues
in C++. In vielen Fällen merkt man es nicht, weil es sich wie erwartet
verhält.
Mit explicit kann man auch verhindern, dass die Konvertierung passiert
ohne dass man es hinschreiben.
https://en.cppreference.com/w/cpp/language/converting_constructor
Vincent H. schrieb:> Das das 6 Posts nach Start des Threads immer noch niemand richtig> beantwortet hat zeigt auf wie sinnvoll Guidelines sind die überall (wos> geht) Uniform-Initialization vorschlagen...
Überall uniform initialization zu verwenden ist Quatsch. Die Syntax hat
ihre Berechtigung, aber int i{3} ist unlesbarer Hipster-Unsinn. Das wird
sich auch nicht ändern, wenn noch 500 Blogposts darüber geschrieben
werden.
Die korrekte Antwort ist im übrigen m.E. meine mit dem QStringLiteral,
denn das ist tatäschlich deutlich schneller als alle anderen Varianten
...
Vincent H. schrieb:> Das das 6 Posts nach Start des Threads immer noch niemand richtig> beantwortet hat zeigt auf wie sinnvoll Guidelines sind die überall (wos> geht) Uniform-Initialization vorschlagen.
Falsch, denn nach dem erstmaligen Aufruf wird mit [...] = [...]; der
Zuweisungsoperator aufgeurfen und kein Konstruktor.
Stefan ⛄ F. schrieb:> Wusste ich noch nicht. Man lernt nie aus.
Und dann schreibst Du C++ Tutorials? Du solltest Dir den Unterschied
zwischen (Kopier-)Zuweisung und Initialisierung klar machen (s.a. Dein
Tutorial). Und den Unterschied zwischen Deklaration und Definition. Das
sind die Basics, die man von einem C++-Tutorial erwartet, was wirklich
sinnvoll ist.
Das ist ein sog. Typumwandlungskonstruktor. Der wird für implizite
Typumwandlungen eingesetzt. Es ist üblich den mit "explicit"
auszuschalten, weil impilizize Tyoumwandlungen meistens unerwünscht sind
(Du hast Dich ja auch gewundert). Ein Typumwandlungs-ctor ist einer, den
man mit einem Argument eines fremden Typs aufrufen kann (BTW: nicht nur
derjenige, der einen Parameter hat).
Es gibt auch das Gegenstück: Typumwandlungsoperatoren. Der bekannteste
ist wohl operator bool() cosnt. Der sollte immer explicit sein, sonst
gibt es an unerwarteter Stelle Überraschungen (Übungsaufgabe: warum?).
Auch hier sieht man mal wieder, warum ich uniform-initialization immer
wieder hier vorschlage, obwohl viele hier im Forum dann ja sofort auf
die Barrikaden gehen. Das hat schon seinen Grund!
Wilhelm M. schrieb:> Und den Unterschied zwischen Deklaration und Definition. Das> sind die Basics, die man von einem C++-Tutorial erwartet, was wirklich> sinnvoll ist.
Nein, das sind Wortklaubereien von möchtegern Deutschlehrern, die dem
Anfänger keinen praktischen Nutzen bringen.
Ich habe nicht ohne Grund am Ende des Artikels empfohlen, ab da mit
einem guten Buch weiter zu machen.
Andere Programmiersprachen nutzen die Begriffe übrigens anders als C++,
was die Sache noch komplizierter macht. Über diese beiden Worte streiten
sich sogar 50 Jährige Entwickler immer noch - wenn sie nicht imstande
sind, ihre Jugendzeit hinter sich zu lassen.
Stefan ⛄ F. schrieb:> Nein, das sind Wortklaubereien von möchtegern Deutschlehrern, die dem> Anfänger keinen praktischen Nutzen bringen.Stefan ⛄ F. schrieb:> Wilhelm M. schrieb:>> Und den Unterschied zwischen Deklaration und Definition. Das>> sind die Basics, die man von einem C++-Tutorial erwartet, was wirklich>> sinnvoll ist.>> Nein, das sind Wortklaubereien von möchtegern Deutschlehrern, die dem> Anfänger keinen praktischen Nutzen bringen.
Das ist jetzt nicht Dein Ernst, oder?
Google mal nach ODR.
Wilhelm M. schrieb:> Auch hier sieht man mal wieder, warum ich uniform-initialization immer> wieder hier vorschlage, obwohl viele hier im Forum dann ja sofort auf> die Barrikaden gehen. Das hat schon seinen Grund!
Erfahrene Entwickler greifen nicht gleich alles auf, was neu ist. Die
haben auch ihre Gründe.
Wilhelm M. schrieb:> Google mal nach ODR.
"The European Online Dispute Resolution (ODR) platform is provided by
the European Commission to make online shopping safer and fairer through
access to quality dispute resolution tools. "
????
Stefan ⛄ F. schrieb:> Wilhelm M. schrieb:>> Google mal nach ODR.>> "The European Online Dispute Resolution (ODR) platform is provided by> the European Commission to make online shopping safer and fairer through> access to quality dispute resolution tools. ">> ????
Scheint heute nicht Dein Tag zu sein: mit google klappt's auch nicht.
Stefan ⛄ F. schrieb:> Erfahrene Entwickler greifen nicht gleich alles auf, was neu ist.
Und Du bist so einer?
In diesem Falle hätte es Dir aber sehr geholfen, wenn Du es richtig
gelernt hättest bzw. durch die Notation sofort unterscheiden hättest
können.
Und "neu" ist hierbei ziemlich relativ ...
Wilhelm M. schrieb:> Stefan ⛄ F. schrieb:>> Wilhelm M. schrieb:>>> Google mal nach ODR.>>>> "The European Online Dispute Resolution (ODR) platform is provided by>> the European Commission to make online shopping safer and fairer through>> access to quality dispute resolution tools. ">>>> ????>> Scheint heute nicht Dein Tag zu sein: mit google klappt's auch nicht.https://de.wikipedia.org/wiki/ODR
Wäre nicht schlecht, wenn du nicht Rätselraten spielen würdest.
Auch wenn es dir Spaß zu machen scheint...
Lothar schrieb:> Wäre nicht schlecht, wenn du nicht Rätselraten spielen würdest.
Ich spiele nicht Rätselraten, sondern ich möchte, dass er was lernt. Und
das tut er nur, wenn er aktiv wird.
Hätte er statt "ODR" etwa "ODR C++" als Suche bei Google verwendet, wäre
es der erste(!) Treffer gewesen. Ist das zu schwer?
Stefan ⛄ F. schrieb:> Nein, das sind Wortklaubereien von möchtegern Deutschlehrern
Sind es nicht – wie du ja hier siehst. Gerade bei C++ ist der
Unterschied zwischen einer Zuweisung und einer Initialisierung u. U.
gewaltig, da es zwei völlig unterschiedliche Aktionen im Code auslösen
kann. Insofern sollte man diesen Unterschied sehr wohl verstanden haben,
auch dann, wenn du diese Wortklauberei ja nicht unbedingt in einem
Tutorial 1:1 unterbringen musst. Allerdings sollte gerade ein Tutorial
auf keinen Fall noch mehr Verwirrung in die Begrifflichkeiten bringen,
indem es an solchen Stellen eben aus Versehen die falschen Worte
auswählt.
Jörg W. schrieb:> Allerdings sollte gerade ein Tutorial> auf keinen Fall noch mehr Verwirrung in die Begrifflichkeiten bringen,> indem es an solchen Stellen eben aus Versehen die falschen Worte> auswählt.
Wo ist es denn falsch?
Stefan ⛄ F. schrieb:> Wo ist es denn falsch?
Habe ich nicht behauptet, ich habe nur festgestellt, dass es mehr als
nur "Krümelkackerei" ist, die richtigen Begriffe zumindest zu kennen,
wenn man anderen etwas beibringen möchte.
Jörg W. schrieb:>> Wo ist es denn falsch?> Habe ich nicht behauptet
Ich würde mich dennoch über konkrete Korrekturhinweise freuen, denn
damit kann ich im Interesse der armen Leser mehr anfangen, als mit
persönlicher Kritik.
Das geht auch an Wilhelm M.
Prinzipiell OK, allerdings ist es natürlich nicht so ganz kurz, und ich
fürchte, für ein vernünftiges Review fehlt mir gerade die Zeit.
Kurz mal drüber geschaut: im Abschnitt "Variablen" sprichst du von einer
Deklaration, wobei es tatsächlich um eine Definition geht – die zwar
zugleich auch deklarative Wirkung hat (d.h. ab da ist sie dem Compiler
innerhalb dieses Sichtbarkeitsbereichs bekannt), aber die Definition
steht hier im Vordergrund.
Unterschied: deklarieren kannst du mehrmals; das macht dem Compiler
etwas bekannt. Oft erfolgt das über eine Header-Datei. Definieren sollte
man nur einmal ¹), an dieser Stelle wird das Objekt tatsächlich erzeugt
und ggf. auch initialisiert.
¹) Historisch in C konnte man globale Definitionen, die keine
Initialisierungen haben (.bss section) auch in mehreren Modulen haben.
Diese wurden dann vom Linker miteinander überlagert. Das ist eine
Remineszenz an FORTRANs COMMON-Blöcke, die zwar vom C-Standard als
mögliche Option abgesegnet ist, aber aus heutiger Sicht außerordentlich
schlechter Programmierstil, den man meiden sollte.
Jörg W. schrieb:> Kurz mal drüber geschaut: im Abschnitt "Variablen" sprichst du von einer> Deklaration, wobei es tatsächlich um eine Definition geht
Danke Jörg
Dirk K. schrieb:> MyString s("Hallo");> -> c'tor with char * parameter called>> MyString s = "Hallo";> -> c'tor with char * parameter called
Es gibt einen wichtigen Unterschied zwischen diesen beiden, aber laut
gcc wohl nur bis C++14. Die zweite Variante braucht den Copy-Contruktor,
auch wenn er ggf. gar nicht aufgerufen wird.
Rolf M. schrieb:> Es gibt einen wichtigen Unterschied zwischen diesen beiden, aber laut> gcc wohl nur bis C++14.
Laut Standard auch. Copy elision ist (für den Fall) erst ab C++17
garantiert. Warum allerdings gcc den copy-Konstruktor in vorherigen
Sprachstandards sehen will, obwohl er offensichtlich doch copy elision
macht, kein Ahnung.
Oliver
Oliver S. schrieb:> Laut Standard auch. Copy elision ist (für den Fall) erst ab C++17> garantiert. Warum allerdings gcc den copy-Konstruktor in vorherigen> Sprachstandards sehen will, obwohl er offensichtlich doch copy elision> macht, kein Ahnung.
Weil das im Sprachstandard so vorgeschrieben ist. Der Compiler darf zwar
das Kopieren des Objekts wegoptimieren, aber da sich die Validität von
Code nicht abhängig von Optimierungseinstellungen ändern darf, muss ein
Copy-Konstruktor trotzdem verfügbar sein, auch wenn er am Ende gar nicht
aufgerufen wird.