Konstruktor/ Zuweisungsoperator :Verständnisfrage



  • Hallo Zusammen,

    Das ist meine erste Frage hier.
    Ich beschäftige mich erst seit kurzem mit Klassen/Operatoren überladen und habe paar Verständnis Fragen. Erstmal nur eine:
    Das ist eine vereinfachte Version der String Klasse aus dem Buch (C++ in 21 Tagen):

    class String
    {
    public:
    	String();
    	String(const char *const);
    	String(const String &rhs);
    	~String();
    
    	//überladene Operatoren
    	String & operator=(const String &);
    
    	// Zugriffsfunktionen
    	unsigned short GetLen() const { return itsLen;}
    	const char *GetString() const { return itsString;}
    private:
    	String(unsigned short);
    	char *itsString;
    	unsigned short itsLen;
    };
    
    String::String()
    {
    	itsString = new char[1];
    	itsString[0] = '\0';
    	itsLen = 0;
    }
    
    String::String(unsigned short len)
    {
    	char *itsString = new char[len+1];
    	for(int i = 0; i<=len; i++)
    		itsString[i] = '\0';
    	itsLen = len;
    }
    
    String::String(const char *const cString)
    {
    	itsLen = strlen(cString);
    	itsString = new char[itsLen+1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = cString[i];
    	itsString[itsLen] = '\0';
    }
    
    String::String(const String &rhs)
    {
    	itsLen = rhs.GetLen();
    	itsString = new char[itsLen + 1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = rhs.GetString()[i];						// checken????
    	itsString[itsLen] = '\0';
    }
    String::~String()
    {
    	delete []itsString;
    	itsLen = 0;
    }
    
    String& String::operator=(const String &rhs)
    {
    	if(this == &rhs)
    		return *this;
    	delete[] itsString;
    	itsLen = rhs.GetLen();
    	itsString = new char[itsLen + 1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = rhs.GetString()[i];
    	itsString[itsLen] = '\0';
    
    	return *this;
    }
    
    int main()
    {
    
    	String s1;
    	char *temp = "test";
    	s1 = temp;
    	printf("s1: %s\n",s1.GetString());
    
    	return 0;
    }
    

    Ich verstehe es nicht warum hier der Kotruktor :

    String(const char *const cString)
    

    Ich hätte verstanden wenn ich so geschrieben hätte:

    char *temp = "test";
    String s1(temp);
    

    Im ersten beispiel weise ich einem Objekt ein String ( = Zeichen). Woher weiss der Compiler dass der entsprechende Kontruktor aufgerufen werden soll?.

    Vielen Dank im voraus



  • Hallo,

    in deinen Frage fehlen irgendwie ein paar Wörter...

    Ich vermute deine Frage ist, warum in der main der String(const char *const cString)-Konstruktor aufgerufen wird?

    Weil bei einer Zuweisung rechts nicht der passende Typ stehen muss, sondern nur ein entsprechend konvertierbarer Typ.
    Das konvertieren passiert über einen passenden Konstruktor.
    Willst du das verhindern, muss der Konstruktor explicit sein.



  • Friend schrieb:

    Hallo Zusammen,

    Das ist meine erste Frage hier.
    Ich beschäftige mich erst seit kurzem mit Klassen/Operatoren überladen und habe paar Verständnis Fragen. Erstmal nur eine:
    Das ist eine vereinfachte Version der String Klasse aus dem Buch (C++ in 21 Tagen):

    class String
    {
    public:
    	String();
    	String(const char *const);
    	String(const String &rhs);
    	~String();
    	
    	//überladene Operatoren
    	String & operator=(const String &);
    
    	// Zugriffsfunktionen
    	unsigned short GetLen() const { return itsLen;}
    	const char *GetString() const { return itsString;}
    private:
    	String(unsigned short);
    	char *itsString;
    	unsigned short itsLen;
    };
    
    String::String()
    {
    	itsString = new char[1];
    	itsString[0] = '\0';
    	itsLen = 0;
    }
    
    String::String(unsigned short len)
    {
    	char *itsString = new char[len+1];
    	for(int i = 0; i<=len; i++)
    		itsString[i] = '\0';
    	itsLen = len;
    }
    
    String::String(const char *const cString)
    {
    	itsLen = strlen(cString);
    	itsString = new char[itsLen+1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = cString[i];
    	itsString[itsLen] = '\0';
    }
    
    String::String(const String &rhs)
    {
    	itsLen = rhs.GetLen();
    	itsString = new char[itsLen + 1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = rhs.GetString()[i];						// checken????
    	itsString[itsLen] = '\0';
    }
    String::~String()
    {
    	delete []itsString;
    	itsLen = 0;
    }
    
    String& String::operator=(const String &rhs)
    {
    	if(this == &rhs)
    		return *this;
    	delete[] itsString;
    	itsLen = rhs.GetLen();
    	itsString = new char[itsLen + 1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = rhs.GetString()[i];
    	itsString[itsLen] = '\0';
    
    	return *this;
    }
    
    int main()
    {
    
    	String s1;
    	char *temp = "test";
    	s1 = temp;
    	printf("s1: %s\n",s1.GetString());
    
    	return 0;
    }
    

    Ich verstehe es nicht warum hier der folgende Konstruktor aufgerufen wird:

    String(const char *const cString)
    

    Ich hätte verstanden wenn ich so geschrieben hätte:

    char *temp = "test";
    String s1(temp);
    

    Im ersten beispiel weise ich einem Objekt ein String ( = Zeichen). Woher weiss der Compiler dass der entsprechende Kontruktor aufgerufen werden soll?.

    Vielen Dank im voraus



  • WTF?
    Warum zitierst du nochmal deinen ganzen Beitrag?



  • Jockelx schrieb:

    Hallo,

    in deinen Frage fehlen irgendwie ein paar Wörter...

    Ich vermute deine Frage ist, warum in der main der String(const char *const cString)-Konstruktor aufgerufen wird?

    Weil bei einer Zuweisung rechts nicht der passende Typ stehen muss, sondern nur ein entsprechend konvertierbarer Typ.
    Das konvertieren passiert über einen passenden Konstruktor.
    Willst du das verhindern, muss der Konstruktor explicit sein.

    Hi Jockelx,

    Danke für die schnelle Antwort.
    Genau das war meine Frage. Ich möchte die Konvertierung nicht verhindern, nur verstehen.

    Wie gesagt, hier kann ich es verstehen, es entspricht der Deklaration des Konstuktors:

    char *temp = "test";
    String s1(temp); //String(const char *const);
    

    Nur bei einer Zuweisung mit '=', verstehe ich es nicht ganz, wie der Compiler drauf kommt dass in diesem Fall der entsprechende Konstruktor aufgerufen werden soll.



  • hustbaer schrieb:

    WTF?
    Warum zitierst du nochmal deinen ganzen Beitrag?

    Mein Gott ein Fehler, ich weiss nicht wie ich das löschen soll. beruhige dich verdammt nochmal



  • Friend schrieb:

    Nur bei einer Zuweisung mit '=', verstehe ich es nicht ganz, wie der Compiler drauf kommt dass in diesem Fall der entsprechende Konstruktor aufgerufen werden soll.

    Hat sich das denn jetzt geklärt?
    Zur Not nochmal:

    a = b

    Gibt es einen A::operator(const A&) aber keinen A::operator(const B&), wird geschaut, ob es denn einen Konstruktor A(const B&) gibt.
    Dann kann A::operator(A(b)) ja aufgerufen werden.



  • Jockelx schrieb:

    Friend schrieb:

    Nur bei einer Zuweisung mit '=', verstehe ich es nicht ganz, wie der Compiler drauf kommt dass in diesem Fall der entsprechende Konstruktor aufgerufen werden soll.

    Hat sich das denn jetzt geklärt?
    Zur Not nochmal:

    a = b

    Gibt es einen A::operator(const A&) aber keinen A::operator(const B&), wird geschaut, ob es denn einen Konstruktor A(const B&) gibt.
    Dann kann A::operator(A(b)) ja aufgerufen werden.

    Ok. mit deiner letzten Erklärung verstehe ich es sehr gut. Aber ich hatte vorher ein anderes Beispiel wo ich den A::operator=(const A&) nicht hatte (nur der konstruktor "Counter(int value)" und es hat trotzdem funktioniert:

    class Counter
    {
    public:
    	Counter():itsValue(0){}
    	Counter(int value) { itsValue = value;}
    	~Counter(){}
    	// Zugriff
    	int GetVal() const { return itsValue;}
    private:
    	int itsValue;
    };
    
    int main()
    {
    	int x = 3;
    	Counter Cnt = x;
    	printf("%d\n",Cnt.GetVal());
    	return 0;
    }
    

    1 - kann es sein dass es daran liegt dass im 2.beispiel (Counter) der Standardzuweisungoperator benutzt wurde, weil ich keinen eigenen definiert habe?

    2 - Falls ja, warum funktioniert es im ersten Beispiel (String) nicht?
    Ich habe als Test den Operrator= entfernt und es hat nicht mehr funktioniert.

    Tut mir Leid dass ich so viele Fragen stelle



  • Friend schrieb:

    1 - kann es sein dass es daran liegt dass im 2.beispiel (Counter) der Standardzuweisungoperator benutzt wurde, weil ich keinen eigenen definiert habe?

    Tatsächlich generiert der Compiler in Deinem Fall einen Zuweisungsoperator und benutzt den auch.
    Allerdings hat das ein Geschmäckle:
    Weil Du offenbar einen Destruktor brauchst ist dieser implizit deklarierte Zuweisungsoperator mit ziemlicher Sicherheit falsch. Dementsprechend ist dieser Fall auch "deprecated" und u.U. werden zukünftige Compiler das anders machen.

    Friend schrieb:

    2 - Falls ja, warum funktioniert es im ersten Beispiel (String) nicht?
    Ich habe als Test den Operrator= entfernt und es hat nicht mehr funktioniert.

    Entfern sowohl die Deklaration in der Klasse als auch die Definition ausserhalb. Beziehungsweise: was hat nicht mehr funktioniert? Crash? Kompiliert nicht?

    Und wie immer: Warnlevel auf Maximum stellen in Deiner IDE.



  • Ähm...da ist noch ein Problem: Dein 2. Beispiel ist anders als das erste.

    int main()
    {
        int x = 3;
        Counter Cnt = x; // copy initialization
        printf("%d\n",Cnt.GetVal());
        return 0;
    }
    

    Da wird Cnt initialisiert mit x - der Zuweisungsoperator bleibt außen vor.



  • Entfern sowohl die Deklaration in der Klasse als auch die Definition ausserhalb. Beziehungsweise: was hat nicht mehr funktioniert? Crash? Kompiliert nicht?

    Das habe ich gemacht, Code sieht jetzt folgendermassen aus:

    class String
    {
    public:
    	String();
    	String(const char *const);
    	String(const String &rhs);
    	~String();
    
    	// Zugriffsfunktionen
    	unsigned short GetLen() const { return itsLen;}
    	const char *GetString() const { return itsString;}
    private:
    	String(unsigned short);
    	char *itsString;
    	unsigned short itsLen;
    };
    
    String::String()
    {
    	itsString = new char[1];
    	itsString[0] = '\0';
    	itsLen = 0;
    }
    
    String::String(unsigned short len)
    {
    	char *itsString = new char[len+1];
    	for(int i = 0; i<=len; i++)
    		itsString[i] = '\0';
    	itsLen = len;
    }
    
    String::String(const char *const cString)
    {
    	itsLen = strlen(cString);
    	itsString = new char[itsLen+1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = cString[i];
    	itsString[itsLen] = '\0';
    }
    
    String::String(const String &rhs)
    {
    	itsLen = rhs.GetLen();
    	itsString = new char[itsLen + 1];
    	for(int i = 0; i<itsLen; i++)
    		itsString[i] = rhs.GetString()[i];						// checken????
    	itsString[itsLen] = '\0';
    }
    String::~String()
    {
    	delete []itsString;
    	itsLen = 0;
    }
    
    int main()
    {
    	String s1;
    	char *temp = "text";
    	s1 = temp;
    	printf("s1: %s\n",s1.GetString());
    	return 0;
    }
    

    Kein Crash, kompiliert auch, allerdings ist mir die Ausgabe ein Rätsel:

    s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1:
    s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1:
    s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1: s1:
    usw....

    Ähm...da ist noch ein Problem: Dein 2. Beispiel ist anders als das erste

    hmm verstehe ich nicht, ist für mich das gleiche Prinzip? im ersten Beispiel wird ein Objekt mit einem String initialisiert, im zweiten wird ein Objekt mit einem int Wert initialisiert, in beiden Fällen durch Zuweisung

    In beidem Beispielen gibt es jetzt keinen "Operator=", 1. Beispiel funktioniert nicht, das 2.schon



  • A a = b

    und

    A a;
    a = b;

    sind unterschiedliche Dinge (trotz vielleicht verwirrender Syntax).
    Im ersten Fall gibt es 'a' noch nicht und ein Objekt wird immer über einen Konstruktor erzeugt.
    Also trotz des '=' wird hier nur der Copy-Ctor aufgerufen.
    Im zweiten Fall gibt es 'a' schon, also wird kein Ctor aufgerufen, sondern der =-Operator.

    Zu deinem Probelm:
    Google 'Regel der grossen 3'.
    Du hast keinen =-Operator mehr und der, der automatisch erzeugt wird, macht nicht das, was du willst.
    (Bei der =-Zuweisung wird ein temporäres Objekt erzeugt, mittels falschem Zuweisungsoperator zugewiesen und sofort wieder gelöscht).



  • Jockelx schrieb:

    A a = b

    und

    A a;
    a = b;

    sind unterschiedliche Dinge (trotz vielleicht verwirrender Syntax).
    Im ersten Fall gibt es 'a' noch nicht und ein Objekt wird immer über einen Konstruktor erzeugt.
    Also trotz des '=' wird hier nur der Copy-Ctor aufgerufen.
    Im zweiten Fall gibt es 'a' schon, also wird kein Ctor aufgerufen, sondern der =-Operator.
    .

    Tatsächlich, wenn ich das so ändere: dann funktioniert's.

    char *temp = "text";
    String s1 = temp;
    printf("s1: %s\n",s1.GetString());
    

    Die Sachte scheint komplizierter zu sein als ich dachte...Verwirrung ist ja fast perfect :), ich lese erstmal bissel mehr drüber, auch "Regel der grossen 3"

    Danke erstmal für eure Hilfe


Log in to reply