Frag zu dem : operator
-
das:
class Array { private: int size; int * array; public: Array(int size) : size(size) , array(new int[size]) //was bedeutet das };entspricht semantisch dem hier:
class Array { private: int size; int * array; public: Array(int size){ this->size = size; array = new int[size]; // das bedeutet das };verbildlichung dessen, was tubos gesagt hat.
-
@code von wma
das ist im uebertragenen sinn gemeint. eine initialisierungsliste ist (angeblich) schneller.
-
Das ist kein Funktionsaufruf.
Bye, TGGC (Fakten)
-
aMan schrieb:
eine initialisierungsliste ist (angeblich) schneller.
Die Dinger haben noch viel schönere Vorteile, so kannst Du zB konstante Membervariablen initialisieren oder die Konstruktoren von Basisklassen aufrufen, oder...
-
Bei der Reihenfolge muss man allerdings aufpassen, die wird nämlich nicht durch die Initialisierungsliste festgelegt! Hier ein Beispiel dafür:
#include <iostream> using namespace std; class foo { public: foo() { cout << "foo" << endl;} }; class bar { public: bar() { cout << "bar" << endl;} }; class foobar { const bar &m_bar; const foo &m_foo; public: foobar() : m_foo(foo()), m_bar(bar()) {} }; int main() { foobar fb; }
-
winheis schrieb:
Bei der Reihenfolge muss man allerdings aufpassen, die wird nämlich nicht durch die Initialisierungsliste festgelegt! Hier ein Beispiel dafür:
#include <iostream> using namespace std; class foo { public: foo() { cout << "foo" << endl;} }; class bar { public: bar() { cout << "bar" << endl;} }; class foobar { const bar &m_bar; const foo &m_foo; public: foobar() : m_foo(foo()), m_bar(bar()) {} }; int main() { foobar fb; }Man muss dazu sagen das es ein Beispiel für die falsche Reihenfolge ist. Wenn man eine Initialisierungsliste verwendet muss man auch auf die Reihenfolge achten.
In dem Fall von winheis ist die Reihenfolge in der Initialisierungsliste falsch. Grundsätzlich ist die Reihenfolge, in der die Variablen in der Klasse stehen (von oben nach unten), zu beachten und müssen auch in dieser initialisiert werden. D.h. auch, dass man eventuell gar nichts Initialisieren muss, wie z.B. bei einem Objekt mit Standard Konstruktor. Dann muss man eben leer initialisieren.
Um das Beispiel hier mal aufzunehmen:
class foo { public: foo() { cout << "foo" << endl;} }; class bar { public: bar() { cout << "bar" << endl;} }; class bla { public: bla(){ cout << "bla" << endl; } }; class foobar { const bar &m_bar; bla m_bla; const foo &m_foo; public: foobar() : m_bar(bar()),m_bla(/*leer*/),m_foo(foo()){} };Worauf man auch achten sollte ist das man, wenn man mit Klassen ableitet und die Initialisierungsliste verwendet folgende Reihenfolge zu beachten hat:
1. Virtuelle Basisklassen initialisieren ( via virtual vererbt )
2. Nicht-Virtuelle Basisklassen initialisieren
3. Member (variablen der Klasse) initialisieren
4. Konstruktorcode der Klasse selbst wird ausgeführtBR
-
evilissimo schrieb:
Worauf man auch achten sollte ist das man, wenn man mit Klassen ableitet und die Initialisierungsliste verwendet folgende Reihenfolge zu beachten hat:
1. Virtuelle Basisklassen initialisieren ( via virtual vererbt )
2. Nicht-Virtuelle Basisklassen initialisieren
3. Member (variablen der Klasse) initialisieren
4. Konstruktorcode der Klasse selbst wird ausgeführtFalsch, -100 Punkte!
-
Falsch, -100 Punkte! schrieb:
evilissimo schrieb:
Worauf man auch achten sollte ist das man, wenn man mit Klassen ableitet und die Initialisierungsliste verwendet folgende Reihenfolge zu beachten hat:
1. Virtuelle Basisklassen initialisieren ( via virtual vererbt )
2. Nicht-Virtuelle Basisklassen initialisieren
3. Member (variablen der Klasse) initialisieren
4. Konstruktorcode der Klasse selbst wird ausgeführtFalsch, -100 Punkte!
Dann schreib wie es richtig ist.

-
evilissimo schrieb:
winheis schrieb:
Bei der Reihenfolge muss man allerdings aufpassen, die wird nämlich nicht durch die Initialisierungsliste festgelegt! Hier ein Beispiel dafür:
Man muss dazu sagen das es ein Beispiel für die falsche Reihenfolge ist. Wenn man eine Initialisierungsliste verwendet muss man auch auf die Reihenfolge achten.
In dem Fall von winheis ist die Reihenfolge in der Initialisierungsliste falsch. Grundsätzlich ist die Reihenfolge, in der die Variablen in der Klasse stehen (von oben nach unten), zu beachten und müssen auch in dieser initialisiert werden. D.h. auch, dass man eventuell gar nichts Initialisieren muss, wie z.B. bei einem Objekt mit Standard Konstruktor. Dann muss man eben leer initialisieren.
Um das Beispiel hier mal aufzunehmen:
Worauf man auch achten sollte ist das man, wenn man mit Klassen ableitet und die Initialisierungsliste verwendet folgende Reihenfolge zu beachten hat:
1. Virtuelle Basisklassen initialisieren ( via virtual vererbt )
2. Nicht-Virtuelle Basisklassen initialisieren
3. Member (variablen der Klasse) initialisieren
4. Konstruktorcode der Klasse selbst wird ausgeführtBR
hier ist zuviel 'müssen' drin. grundsätzlich ist die reihenfolge der ctor-initialisierer in der ctor-initialisierungsliste irrelevant - die reihenfolge der initialisierung wird ausschliesslich durch die deklaration der basisklassen und member bestimmt. aus diesem grunde ist es empfohlene praxis die reihenfolge in der liste entsprechend zu gestalten - das ist aber wie gesagt nur guter stil, kein zwang. ausserdem müssen nur solche member/basen aufgeührt werden, welche nicht default-initialisierbar sind.
3. besteht für PODs (und arrays aus PODs) ein semantischer unterschied je nachdem ob sie in der initialisiererliste auftauchen oder nicht: wenn sie nicht in der liste sind, werden sie gar nicht initialisiert, sonst value-initialisiert.weitaus schlimmer als die fehlerhafte reihenfolge finde ich an diesem beispiel den vorgang selbst: ein temporäres objekt an eine member-referenz zu binden. immerhin werden solche objekte beim verlassen des konstruktors zerstört. das macht in dem beispiel nichts, da wir nichts weiter mit dem objekt tun - es sollte aber nicht unkommentiert bleiben

-
camper schrieb:
weitaus schlimmer als die fehlerhafte reihenfolge finde ich an diesem beispiel den vorgang selbst: ein temporäres objekt an eine member-referenz zu binden.
Ich dachte bei const-Referenzen sei das kein Thema. Ich weiss, das ist keinerlei Garantie, aber GCC 3.4.5 beschwert sich bzgl. des Standards nur bei der Übergabe von temporären Objekten an nicht-const-Referenzen. Ich habe leider weder den Standard noch den Stroustrup. Kann jemand sagen, wie das festgelegt wurde?
-
Ich habe mal ein bißchen gesucht, und laut "Thinking in C++" ist es erlaubt Temporaries an const-Referenzen zu binden: http://www.lsw.uni-heidelberg.de/users/sbrinkma/TICPP1/Chapter08.html#Index1532
-
Jetzt habe ich noch mal einen Test gemacht. camper hatte vollkommen Recht! Ist zwar erlaubt, aber ist Kockolores, weil sofort nach der Initialisierung der Destruktor des Temporär-Objekts aufgerufen wird.
-
wer es nachlesen will: 12.2/5
zwar können temporaries an referenzen auf const gebunden werden (und referenzen auf non-const im falle von non-const lvalue temporaries - wie beim fangen von exceptions) - aber im falle von member-referenzen verlängert das die lebensdauer der temporaries nur bis zum ende des konstruktors. der grund dafür ist relativ simpel: im falle von lokalen oder statischen referenzen exisitiert die referenz im selben scope wie das temporäre objekt - d.h. wenn der block verlassen wird, steht definitiv fest, dass diese an ein temporäres objekt gebunden ist - folglich ist es für den compiler sehr einfach, an dieser stelle den destruktor des temporären objekts aufzurufen. im falle des bindens an memberreferenzen geht das nicht. wenn der destruktor des objekts, das die referenz enthält, verlassen wird, fehlt dem compiler der kontext. er kann dann nicht wissen, wie das objekt, an das die referenz gebunden ist, erzeugt wurde - es könnte ein temporäres objekt gewesen sein, oder auch nicht. aber nur im ersteren falle würden wir das objekt zerstören wollen. der compiler müsste also zusätzliche infomationen speichern - und es würde weitaus schwieriger vorauszusagen - was beim ausführen des destruktors passiert. mit der regel, so wie sie ist dagegen, wissen wir stets, dass beim zerstören von memberreferenzen nichts passiert. kleines beispiel:
#include <vector> #include <ctime> #include <cstdlib> struct Bar {}; struct Foo { const Bar& bar_; Foo(const Bar& bar) : bar_( bar ) {} }; int main() { using namespace std; srand( time( 0 ) ); Bar b; vector< Foo > v; for ( int i = 0; i < 10; ++i ) v.push_back( rand() % 2 ? Foo( b ) : Foo( Bar() ) ); }verlängerung dre lebenszeit von temporaries bis zum destruktor des objekts, das die referenz enthält, würde die sache im destruktor doch wesentlich komplizierter machen.