Anfängerfragen
-
Kurze Zwischenfrage: Warum hast du den Konstructor explicit gemacht?
-
Ruvi++ schrieb:
Kurze Zwischenfrage: Warum hast du den Konstructor explicit gemacht?
um implizite Konversionen von int nach Raumschiff zu verhindern.
-
daddy_felix schrieb:
Ruvi++ schrieb:
Kurze Zwischenfrage: Warum hast du den Konstructor explicit gemacht?
um implizite Konversionen von int nach Raumschiff zu verhindern.
Entschuldige, irgendwie steh ich auf dem Schlauch.
Wie kann das denn in dem geschriebenen Code passieren?
Ich uebergebe doch einfach einen int Parameter als Argument an den Konstruktor der damit eine interne Variable initialisiert?
-
Damit verhindert SeppJ sowas:
std::vector<Raumschiff> v; v.push_back( 1 );Taucht im Code so zwar nicht auf, aber wer weiß, was TE noch so alles vorhat.
-
Ruvi++ schrieb:
Wie kann das denn in dem geschriebenen Code passieren?
Ganz einfach:
void foo( Raumschiff );Nun ist es möglich, foo mit 4 als Argument aufzurufen. Das ist aber definitiv nicht wünschenswert, da wir ja wollen, dass im Code explizit steht, dass das Argument ein
Raumschiffinitialisiert.Edit: Zu spät.
-
Ein paar Beispiele:
struct Foo { explicit Foo(int){} }; struct Bar { Bar(int){} }; void foobar(Foo, Bar); int main() { Foo f1, f2 = 5, f3(6), f4 = static_cast<Foo>(7), f5 = (Foo) 8; Bar b1, b2 = 5, b3(6), b4 = static_cast<Bar>(7), b5 = (Bar) 8; foobar(f1,b1); foobar(1,2); foobar(f1,2); foobar(1,b1); }Welche gehen, welche nicht? Sind die wohl alle so gewünscht, wenn Foo/Bar ein Raumschiff modellieren?
Ein etwas extremeres, aber praxisnäheres, Beispiel ist std::vector:
explicit vector (size_type n);Wenn das nicht so wäre, könnte man:
vector<string> foo = 3;Sicherlich nicht gewollt.
edit: viel zu spät...
-
Erstmal vielen Dank!
Aber ich bin ehrlich gesagt ein wenig verwirrt.
Mir war ueberhaupt nicht bewusst, dass einige Sachen die ihr da geschrieben habt ueberhaupt funktionieren.Ich waere niemals auf die Idee gekommen sowas zu schreiben:
Mir ist auch nicht verstaendlich warum, das ueberhaupt funktioniert:Bar b2 = 5;Ich bin bis jetzt auch noch nie auf die Idee gekommen sowas zu schreiben:
Foo f4 = static_cast<Foo>(7); Foo f5 = (Foo) 8;Bedeutet, dass das hier eure Konstruktoren so gut wie immer explizit macht?
-
Ruvi++ schrieb:
Bedeutet, dass das hier eure Konstruktoren so gut wie immer explizit macht?
Das kommt drauf an. Wenn das, was als Parameter genommen wird, nicht direkt etwas mit der erzeugten Klasse zu tun hat, dann auf jeden Fall explicit. Dieser Fall liegt hier vor, denn man hat einen Integer und ein Raumschiff, der Integer soll aber nicht wie ein Raumschiff benutzt werden können.
Wenn die Klasse aber etwas darstellt, das mit dem Parametertyp verwandt ist, dann will man nicht unbedingt ein explicit. Beispiel eine Bruchzahlklasse. Da will ich auch andere Zahlen einsetzen können, wo eine Bruchzahl erwartet wird, die dann ggf. umgewandelt werden soll. Daher einen Konstruktor, der einen arithmetischen Typ nimmt, hier nicht explicit.
Den zweiten Fall hat man in der Praxis eher seltener als den ersten.
-
Ruvi++ schrieb:
Ich waere niemals auf die Idee gekommen sowas zu schreiben:...
Man denkt nicht unbedingt in jeden Moment logisch, vielleicht ist man einfach abgelenkt usw. Und glaub mir: das kann auch mal im Produktivcode passieren (wie bei uns kürzlich bei der Umstellung von char => wchar_t...).
-
Ruvi++ schrieb:
Foo f4 = static_cast<Foo>(7); Foo f5 = (Foo) 8;Das war didaktisch etwas ungünstig gewählt, da ich schreibfaul war. Das ist keine typische Initialisierung. Besser wäre etwas in dieser Richtung:
Foo f1, f2 = 5, f3(6); Bar b1, b2 = 5, b3(6); foobar(f1,b1); foobar(1,2); foobar(f1,2); foobar(1,b1); foobar(static_cast<Foo>(7), static_cast<Bar>(7)); foobar((Foo) 8, (Bar) 8);Ruvi++ schrieb:
Mir ist auch nicht verstaendlich warum, das ueberhaupt funktioniert:
Bar b2 = 5;Interessante Frage, die meisten Leute würden wohl eher fragen, wieso das bei Foo nicht funktioniert. Wieso geht das bei Bar? Nun, weil der Standard sagt, dass man Variablen so initialisieren darf. Das nennt man "direct intialization" und "copy initialization". Der subtile Unterschied ist: Bei der direkten Initialisierung wird direkt der passende Konstruktor aufgerufen, sofern er existiert. Bei der copy initialization muss hingegen das, was auf der rechten Seite steht, über implizite Umwandlungen in den passenden Typ umgewandelt werden. Das geht hier bei Bar, da es eine solche Umwandlung gibt, bei foo jedoch nicht. Außerdem muss der Kopierkonstruktor zur Verfügung stehen. Er wird zwar wegoptimiert werden, aber er muss verfügbar sein, damit die Aktion stattfinden kann (spielt hier aber keine Rolle).
In keinem dieser Fälle wird jemals der Zuweisungsoperator angepackt, obwohl ein Gleichheitszeichen benutzt wird.P.S.: Wieso das die meisten Leute andersrum fragen würden? Weil sie wissen, dass folgendes keinen Unterschied macht:
double d(5); double e = 5;Macht beides das gleiche und es ist dem double auch egal, dass die 5 ein int ist.
-
SeppJ schrieb:
P.S.: Wieso das die meisten Leute andersrum fragen würden? Weil sie wissen, dass folgendes keinen Unterschied macht:
double d(5); double e = 5;Macht beides das gleiche und es ist dem double auch egal, dass die 5 ein int ist.
Das war mir bewusst aber irgendwie war ich der Meinung, das gilt nur fuer "normale" Datentypen.
Eine Klasse kann ja auch mehrere Argumente im Konstruktor benoetigen.
struct Bar { int a; int b; int c; Bar(int aa, int bb, int cc) : a(aa), b(bb), c(cc){ cout << a << endl; cout << b << endl; cout << c << endl; } };Wie sieht denn in dem Fall die Konstruktion mit "=" aus?
-
Ja rate mal

Geht nicht.
-
DocShoe schrieb:
Ja rate mal

Geht nicht.
Ok, dann habe ich mich nicht zu doof angestellt.
Ich persoenlich finde es ein wenig befremdlich, dass der Sprachstandard von C++ eine Konstruktion von Structs und Klassen mit "=" ueberhaupt erlaubt.
Gibt es dafuer einen uebergeordneten Grund?
Ich habe irgendwie Schwierigkeiten mir das logisch zu erschliessen.Denn die Konstruktion mit "=" scheint ja nur zu funktionieren, wenn der Konstruktor nur ein Argument benoetigt?
Ich persoenlich bin bis dato davon ausgegangen, dass es nicht geht.
Danke nochmal an alle fuer die umfangreichen Erklaerungen.
-
Ruvi schrieb:
Ich persoenlich finde es ein wenig befremdlich, dass der Sprachstandard von C++ eine Konstruktion von Structs und Klassen mit "=" ueberhaupt erlaubt.
Gibt es dafuer einen uebergeordneten Grund?
Ich habe irgendwie Schwierigkeiten mir das logisch zu erschliessen.Nun, in C konnte und kann man Variablen nur so initialisieren.
Und mal ehrlich, du initialisierst std::string doch nicht ernsthaft so, oder?std::string abc("abc");Bzw, schreibst doch auch nicht:
Foo f(func());
-
Nathan schrieb:
Und mal ehrlich, du initialisierst std::string doch nicht ernsthaft so, oder?
std::string abc("abc");Autsch.
Ok, erwischt daran hatte ich nicht gedacht.
Ich nehm alles zurueck und behaupte das Gegenteil.
-
Noch ne kleine Frage, was spricht dagegen einen zeiger mit null zu initialisieren?
-
Kamelkackeoutlogged schrieb:
Noch ne kleine Frage, was spricht dagegen einen zeiger mit null zu initialisieren?
Die Frage ganz ohne Kontext: Nichts. Man initialisiert Objekte mit den Werten, die sie haben sollen, das gilt für Zeiger wie für alle anderen auch. Man sollte daher natürlich einen Grund haben, wieso man den Zeiger mit 0 initialisiert und dieser Grund sollte nicht sein "ich hab das mal so gesehen". Leider ist letzteres bei Zeigern erstaunlich oft der Fall. Vielleicht weil Leute Angst davor haben, sich mit dem als schwierig geltenden Thema auseinanderzusetzen und deswegen blind nachmachen, was sie sehen. Bei anderen Objekten käme kaum jemand auf die Idee ihnen einfach mal irgendwas ohne Grund zuzuweisen.
Der Anfangsbeitrag hier ist ein hervorragendes Beispiel:
account *kunde = NULL; kunde = new account;Warum wird hier der Umweg über Null gemacht? Warum nicht
account *kunde = new account;?
(Die wahre Frage ist natürlich, warum überhaupt new? Aber das klammere ich bei einer Frage nach Zeigerinitialisierung mal aus.)