Frage zu Klassen



  • Hi ich habe jetzt im Internet ein paar Sachen über klassen gelesen und wollte mal fragen ob ich das jetzt alles mit Konstruktor usw. richtig verstanden habe.

    class dog
    {
    private: 
    	int age;
    	string name;
    public:
    	dog(int newAge = 0, string newName = "dog")
    	{
    		age = newAge;
    		name = newName;
    	}
    	~dog
    	{
    		age = 0;
    		name = "";
    	}
    	void SetAge(int newAge)
    	{
    		age = newAge;
    	}
    	int getAge(void)
    	{
    		return age;
    	}
    	void setName(string newName)
    	{
    		name = newName;
    	}
    	string getName(void)
    	{
    		return name;
    	}
    };
    

    Ist der Konstruktor hier richtig verwendet? Habe es nämlich auch schon anderes gesehen, dort wurde es irgendwie so gemacht:

    dog(bla bla): age = 0
    {
    
    }
    

    order so ähnlich ^^

    Was davon ist richtig? Oder kann man beides benutzten?



  • dog(int newAge, string newName)
    {
      age = newAge;
      name = newName;
    }
    

    Hier wird name zunächst mit dem Standardkonstruktor initialisiert, also ein leerer string und age mit nichts initialisiert, hat irgendeinen undefinierten Wert. Im Rumpf des ctors werden dann Werte zugewiesen.

    dog(int newAge, string newName)
    : age(newAge), name(newName)
    {
    }
    

    Hier wird age direkt mit dem Wert newAge initialisiert und für name der Kopierkonstruktor aufgerufen. (Bei neueren Compilern evtl. auch der "Move-konstruktor")

    dog(int newAge, const string& newName)
    : age(newAge), name(newName)
    {
    }
    

    dito, spart aber das Kopieren des Arguments newName.



  • also geht das was ich oben geschrtieben habe nicht?

    Hab mir das so gedacht das wenn mann halt nichts besonderes angibt das Objekt die werte 0 und dog bekommt, und das das funktioniert auch wunderbar, aber ist das falsch wie ich es gemacht habe?



  • Nicht falsch, aber schlecht. Die Defaultwerte sind schon okay, aber du solltest auf jeden Fall die Initialisierungsliste verwenden.



  • ok dann hab ich noch eine frage wozu sind dann die {} bei dem Konstruktor?^^



  • Falsch ist das nicht, blos nicht besonders effektiv, weil du da wie erklärt mehr Aufrufe hast. Das hinter dem Doppelpunkt nennt sich Initialisierungsliste und sollte man immer benutzen. An manchen Stellen wird sie sogar zwingend notwendig, nämlich wenn Basisklassen oder Member über keinen Standardkostruktor verfügen.

    Der Rumpf deines Destruktors ist auch nicht falsch aber vollkommen sinnlos. Das kann einfach weg.
    Und dann kannst du dir auch nochmal das Thema "Referenzen" ansehen. Objekte wie string sollte man fast immer als const string& übergeben. Genauso kannst du besser schreiben:

    const std::string& getName(void)const
    {
      return name;
    }
    


  • wurstuk schrieb:

    ok dann hab ich noch eine frage wozu sind dann die {} bei dem Konstruktor?^^

    Jede Funktion, die man aufrufen möchte braucht einen Funktionsrumpf. Auch wenn da nichts drinsteht.



  • OK danke^^



  • wurstuk schrieb:

    class dog
    {
    private: 
    	int age;
    	string name;
    public:
    	dog(int newAge = 0, string newName = "dog")
    	{
    		age = newAge;
    		name = newName;
    	}
    

    Falsch ist das nicht. Aber auch nicht besonders gut. Besser:

    explicit dog(int newAge=0, string newName="dog")
    : age(newAge)
    {
      name.swap(newName);
    }
    

    Mit explicit wird hier verhindert, dass ein int versehentlich in ein dog konvertiert werden kann. :age(newAge) ist die Initialisierungssyntax. Ich habe hier aber name absichtlich außen vor gelassen. Deswegen wird das Subobjekt name default-konstruiert. Später vertausche ich den Inhalt zwischen name und dem Parameter newName . Das geht bei string typischerweise flotter als Kopieren/Zuweisen.

    wurstuk schrieb:

    ~dog
    	{
    		age = 0;
    		name = "";
    	}
    

    Dieser Destruktor erfüllt keinen Zweck. Du kannst ihn weglassen.

    wurstuk schrieb:

    int getAge(void)
    	{
    		return age;
    	}
    

    Das void in den Klammern ist bei C++ überflüssig. Du kannst es weglassen. Da diese Funktion den logischen Zustand des Objekts nicht verändern will/soll, kannst Du das mit einem zusätzlichen const kennzeichnen:

    int getAge() const
    { return age; }
    

    Es lohnt sich, so etwas konsequent durchzuziehen. Das gilt auch für Deine getName-Funktion.

    wurstuk schrieb:

    void setName(string newName)
    	{
    		name = newName;
    	}
    

    Hier könntest Du aus newName noch eine Referenz machen:

    void setName(string const& newName)
    { name = newName; }
    

    Das Kopieren von string -Objekten, sowie andere Standardcontainer kostet etwas Zeit. Da Du newName nicht als Kopie des Arguments brauchst und nur lesend auf das Quell-Objekt zugreifen willst, kann man auch einfach eine ref-auf-const benutzen. Der Aufruf der setName-Funktion sieht aber immer noch genauso aus. So kann man u.U. unnötige Kopien vermeiden. Bei anderen Typen (int, double, etc) lohnt sich so etwas allerdings nicht.

    kk



  • OK vielen Danke! 😃


Anmelden zum Antworten