Konstruktor bei C++ (kleine Syntax-Frage)



  • Hallo Leute,

    ich komme aus der Java-Welt und habe eine kleine Frage zur Syntax eines C++ Konsturktors...

    Dafür habe ich mir jetzt eine kleine Klasse ausgedacht...
    Die Klasse soll Mensch heißen und hat zwei Atribute: Alter und Name.

    Also in Jave würde ein Konstruktor bei mir wie folgt ausschauen:

    Mensch (int alter, String name) {
    this.alter = alter;
    this.name = name;
    }
    

    So wenn ich das Ganze jetzt in C++ mache habe ich mir jetzt erst mal folgedes ausgedacht:

    Mensch (int alter, char* name) {
    this->alter = alter;
    strcpy(this->name ,name);
    }
    

    Das ganze scheint auch gut zu funktionieren, doch jetzt gibt es ja bei C++ auch die möglichkeit eine Intitialisierungsliste anzugeben...
    Also angenommen ich würde einen Konstrukor mit einer Initialisierungsliste erstlelen wollen der nur das Atribut name setzt, dann würde das ja so aussehen:

    Mensch (int alter)
    :alter(alter) {
    }
    

    Da habe ich jetzt auch schon zwei Fragen, wann benutze ich diese Art der Initialisierung? Was ist der tiefere Sinn dahinter und was ist "besser"?
    Meine zweite Frage dreht sich um die Namen der Variablen, ich habe es mir bei Java immer angewöhnt beim konstruktor die Übergabeparameter genau so zu nennen wir die Variablen der Klasse (so wie in dem Beispiel).
    Im ersten Beispiel habe ich auch kein Problem damit mit "this->" ist ja auch klar welche Variable immer gemeint ist...
    Im zweiten aber kann ich nicht schreiben "this->alter(alter)" das scheint wohl falsch zu sein, daher meine Frage...
    Weiß der Compiler automatisch welche Variable gemeint ist?
    In Visual Studio scheint das kein Problem zu sein, doch in NetBeans meckert die Umgebung sagt das geht nicht weil sie nicht verstehen dass die erste Variable die Klassenvariable ist... ):
    Also wie macht man das üblicherweise in C++?

    Noch etwas, beim Namen scheint das irgendwie mit der Initialisierungsliste nicht zu funktionieren:

    Mensch (char* name)
    :name(name) {
    }
    

    auch nicht in dieser Variante:

    Mensch (char* name)
    :name(*name) {
    }
    

    Heißt das jetzt bei chars sollte man keine Initialisierungsliste nutzen?
    Ich hoffe Jemand kann mir das etwas erklären, sowohl das mit der Syntax und auch das mit der Initialisierungsliste...

    Vielen Dank!



  • Der Vorteil einer Initialisierungsliste ist der, dass du mit ihr deine Membervariablen einen Initialisierungswert geben kannst.
    Wenn du im Konstruktorrumpf deinen Membervariablen einen Wert zuweist ist das nur eine Zuweisung und KEINE Initialisierung.



  • Letztens hat einer das Gleiche gefragt:
    http://www.c-plusplus.net/forum/301712

    Außerdem ist char* hier unötig, benutzt lieber std::string.

    #include <iostream>
    #include <string>
    
    class foo
    {
        public:
            foo(std::string& _bar) : bar(_bar) {}
    
        private:
            std::string bar;
    };
    
    int main()
    {
        foo("hallo");
    }
    

    Hast du ein bestimmtes Buch um C++ zu lernen ?



  • Ah okay, vielen Dank für die Info und den Link!

    Ich weiß jetzt das man Initialisierungslisten nutzen sollte,
    doch was genau ist daran schlecht wenn man nicht sofort initialisiert?

    Nein ich habe kein Buch, ich wollte mir erst mal bisschen C++ anschauen und dann später mir ein Buch besorgen...

    Aber noch zu meiner Syntax-Frage, gibt es da einen guten "Programmeirstiel" wie man das machen sollte?

    Also folgendes Beispiel...

    Ich würde meinen Konstruktor so schreiben:

    Mensch (string& name, int alter)
    : name(name), alter(alter) {
    }
    

    Der funktioniert auch so in Visual Studio, doch in NetBeans z.B. nicht...
    Da müsste ich dann sowas hier machen damit er geht:

    Mensch (string& _name, int _alter)
    : name(_name), alter(_alter) {
    }
    

    Wie sollte man es machen?
    Ist die erste Variante "unüblich" bei C++?



  • Alle modernen Compiler sollten das richtig auflösen, auch wenn Parameter und Member hier den gleichen Namen haben. (Im Funktionskörper selbst geht das natürlich nicht mehr, da müsstest du im Notfall über this-> zugreifen.)

    Die Vorteil der Initialisierungsliste ist, dass man damit eben initialisiert und nicht zuweist. Das mag trivial klingen (und für Integer etc. ist es das auch meistens), ist es aber nicht immer. Angenommen du hast als Member ein Objekt einer Klasse ohne default Konstruktor. Ohne das Objekt vernünftig zu initialieren kannst du das nicht kompilieren.
    Zudem kann eine direkte Initialisierung auch schneller sein, als eine Default-Initialisierung gefolgt von einer Zuweisung. (z.B. nur eine Speicheranforderung statt zwei etc. pp.)
    Abgesehen davon ist es hübscher. 😉

    Du solltest dir weiterhin angewöhnen auf die "const correctness" zu achten. Wenn du einen Pointer oder eine Referenz auf etwas erwartest, weiß der Aufrufer ja nicht, ob du das veränderst. Wenn du es also nicht verändest (nur liest), solltest du eine const&/const* erwarten.

    Also z.B. const char* (pointer to const char) bzw. const std::string& (reference to const std::string).



  • vandread schrieb:

    wann benutze ich diese Art der Initialisierung?

    Die Initialisierungsliste solltest du, falls möglich, immer verwenden. Bei folgenden Dingen musst du sie sogar zwingend verwenden, sonst bekommst du einen Compilerfehler:
    -const-qualifizierte Member
    -Referenzen
    -Member ohne Default-Konstruktor
    -Wenn die Basisklasse keinen Default-Konstruktor hat.

    vandread schrieb:

    Was ist der tiefere Sinn dahinter und was ist "besser"?

    Besser daran ist, wie es der Name schon sagt, dass eine Initialisierungsliste die Member initialisiert, du also nur einen Methodenaufruf brauchst. Ohne Initialisierungsliste hast du 2 Methodenaufrufe, nämlich einen für die Definition und einen für die Zuweisung, was ziemlich doof ist. Hier mal ein sehr einfaches Beispielprogramm:

    #include <iostream>
    class foo
    {
    	public:
    		foo() 
    		{
    			std::cout << "Definition" << '\n';
    		}
    		foo(int i) 
    		{
    			std::cout << "Initialisierung" << '\n';
    		}
    		void operator=(int i)
    		{
    			std::cout << "Zuweisung" << '\n';
    		}
    };
    class Mensch
    {
        private:
            foo f_1;
            foo f_2;
    	public:
    		Mensch() : f_1(7)
    		{
    			f_2 = 7;
    		}
    };
    int main()
    {
        Mensch m;
        return 0;
    }
    

    Wie du siehst, bekommst du bei f_2 zwei Ausgaben, für f_1 nur eine.

    vandread schrieb:

    ich habe es mir bei Java immer angewöhnt beim konstruktor die Übergabeparameter genau so zu nennen wir die Variablen der Klasse (so wie in dem Beispiel). Im zweiten aber kann ich nicht schreiben "this->alter(alter)" das scheint wohl falsch zu sein, daher meine Frage... Weiß der Compiler automatisch welche Variable gemeint ist?

    Ja, weiß er, und zwar einfach aus dem Grund, dass die Initialisierungsliste ausschließlich Member der eigenen Klasse initialisieren kann. Und damit ist auch klar, welche Variable die Membervariable und welche der Parameter ist. (siehe auch http://www.c-plusplus.net/forum/302911)
    Gemacht wird es also ganz genau so, wie du es hast:

    Mensch(int alter) : alter(alter) {}
    

    vandread schrieb:

    Noch etwas, beim Namen scheint das irgendwie mit der Initialisierungsliste nicht zu funktionieren:

    Mensch (char* name) : name(name) {}
    

    Am Besten vergisst du das ganz schnell wieder und machst es auf die C++ Art:

    #include <string>
    class Mensch
    {
    	private:
    		std::string name;
    	public:
    		Mensch(const std::string& name) : name(name) {}
    };
    int main()
    {
    	Mensch m("Max");
    	return 0;
    }
    


  • Alles klar (:
    Vielen Dank Leute!



  • Gugelmoser schrieb:

    ...

    👍

    Gugelmoser schrieb:

    vandread schrieb:

    Noch etwas, beim Namen scheint das irgendwie mit der Initialisierungsliste nicht zu funktionieren:

    Mensch (char* name) : name(name) {}
    

    Am Besten vergisst du das ganz schnell wieder und machst es auf die C++ Art:

    #include <string>
    class Mensch
    {
    	private:
    		std::string name;
    	public:
    		Mensch(const std::string& name) : name(name) {}
    };
    int main()
    {
    	Mensch m("Max");
    	return 0;
    }
    

    oder auf die "C++11 Art" mit etwas mehr pass-by-value, falls sinnvoll:

    #include <string>
    #include <utility>
    
    class Mensch
    {
        std::string name;
    public:
        Mensch(std::string name)
        : name(std::move(name))
        {}
    };
    

    (-:


Anmelden zum Antworten