Konstruktor oder Operator "="



  • einfach testen:

    #include <iostream>
    
    struct CString
    {
        CString() { std::cout << "1\n"; }
        CString(const CString&) { std::cout << "2\n"; }
        CString& operator=(const CString&) { std::cout << "3\n"; }
    };
    
    int main()
    {
        CString cs2;
        std::cout << "-\n";
        CString cs1;
        cs1 = cs2;
        std::cout << "-\n" << std::flush;
        CString cs3 = cs2;
    }
    
    1
    -
    1
    3
    -
    2
    

    mfg.



  • Dann frage ich mich, warum dieses geht

    CString cs2 = CString::IntToHex(i, 8);
    

    folgendes aber nicht:

    CString cs2( CString::IntToHex(i, 8) );
    

    Da sagt er mir: "[C++Fehler] Project1.cpp(149): Could not find a match for 'CString::CString(CString)'."



  • #include <iostream>
    
    struct CString
    {
        CString() { std::cout << "1\n"; }
        CString(const CString&) { std::cout << "2\n"; }
        CString& operator=(const CString&) { std::cout << "3\n"; return *this; }
    
        static CString makeString(void) { return CString(); }
    };
    
    int main()
    {
        CString cs2;
        std::cout << "-\n";
        CString cs1;
        cs1 = cs2;
        std::cout << "-\n";
        CString cs3 = cs2;
        std::cout << "-\n";
    
        CString cs4( CString::makeString() );
        CString cs5 = CString::makeString();
    }
    

    Funktioniert einwandfrei. Nutzt Du zufällig VC++6?

    EDIT:
    Funzt selbst mit dem. Welche Signatur hat denn CString::IntToHex?



  • Ne, Borland C++Builder.

    class CString
    {
    private:
        LPTSTR m_String;
        unsigned int m_BufSize;
    
        void MakeNew(unsigned int size);
    
    public:
        // Konstruktoren
        CString(LPTSTR str);
        CString(LPCTSTR str);
        CString(CString& str);
        CString(int length);
        CString();
    
        // Destruktor
        ~CString();
    
        // Hauptmethoden
        int Length();
        bool Delete(unsigned int index, unsigned int count); // index is zero based
        void Insert(CString& str, unsigned int index); // index is zero based
        void UpperCase();
        void LowerCase();
        int LoadString(HINSTANCE hInstance, UINT uiID);
        int Compare(CString& rhs, bool case_sensitive);
        CString SubString(unsigned int index, unsigned int count); // index is zero based
        int ToInt(); // Encapsulates atoi(), i.e. ToInt() == 0 if m_String is no integer value
        double ToDouble();          // Trennzeichen ist der Punkt (.)
        long double ToLongDouble(); //  -"-
        LPWSTR ToWideChar(LPWSTR dest, int destSize);
        LPSTR ToMultiByte(LPSTR dest, int destSize);
        int FirstPositionOf(CString& subStr);
        int LastPositionOf(CString& subStr);
        LPTSTR c_str();
        bool IsEmpty();
        int GetWindowText(HWND hwnd);
        int SetWindowText(HWND hwnd);
    
        // Statics
        static CString IntToHex(unsigned int value, unsigned int digits);
    
        // Operatoren
        void operator =(CString& rhs);
        void operator =(LPTSTR str);
        void operator =(LPCTSTR str);
        bool operator ==(CString& rhs) const;
        bool operator ==(LPTSTR str) const;
        bool operator ==(LPCTSTR str) const;
        CString operator +(CString& rhs) const;
        CString operator +(LPTSTR str) const;
        void operator +=(CString& rhs);
        TCHAR operator [](int index); // index is zero based
    };
    


  • Alles klar:
    Der Kopierkonstruktor muss eine konstante Referenz auf CString annehmen, keine normale, da Du hier ein temporäres Objekt erzeugst (nämlich das Ergebnis von IntToHex, welches solange lebt bis der Konstruktor durchgelaufen ist). Dein "Copy-Constructor" ist strenggenommen nämlich garkeiner, sondern nur ein Non-Default-Constructor.

    CString(const CString& str);
    

    Wobei der VC++6 das sogar durchgehen lässt 😃



  • OK, danke. Dann bekomme ich aber in

    CString::CString(const CString& str)
    {
       m_BufSize = str.Length() + 1;
       m_String = (LPTSTR)new TCHAR[m_BufSize];
       lstrcpy(m_String, str.c_str());
    }
    

    die folgenden Warnungen:

    [C++Warnung] CString.cpp(53): Non-const function CString::Length() called for const object.
    [C++Warnung] CString.cpp(55): Non-const function CString::c_str() called for const object.
    


  • Du kannst auf die Memberelemente von str direkt zugreifen, ohne die Accessor-Methoden. Das hat den Vorteil dass er selbst merkt dass diese const sind und entsprechend reagiert.

    Die Methode Length (z.B.) ist nicht const markiert, daher weiss der Compiler nicht, dass die Methode Length keine Änderungen an dem Objekt vornimmt. Du kannst auf einem konstanten Objekt nur Konstante Methoden aufrufen.

    Also Möglichkeit 1:

    CString::CString(const CString& str)
    {
       m_BufSize = strlen(str.m_String) + 1;
       m_String = (LPTSTR)new TCHAR[m_BufSize];
       lstrcpy(m_String, str.m_String);
    }
    

    Und Möglichkeit 2:

    int Length() const;
    

    Wobei Möglichkeit 2 generell immer genutzt werden sollte, wenn die Methode das Objekt tatsächlich nicht ändert. (Gutes Suche- oder Google-Stichwort: const-correctness)


  • Mod

    LordJaxom schrieb:

    Alles klar:
    Der Kopierkonstruktor muss eine konstante Referenz auf CString annehmen, keine normale, da Du hier ein temporäres Objekt erzeugst (nämlich das Ergebnis von IntToHex, welches solange lebt bis der Konstruktor durchgelaufen ist). Dein "Copy-Constructor" ist strenggenommen nämlich garkeiner, sondern nur ein Non-Default-Constructor.

    CString(const CString& str);
    

    Wobei der VC++6 das sogar durchgehen lässt 😃

    das ist schon ein copy-konstruktor. der standard erlaubt 4 verschiedene signaturen, die einen copy-konstruktor darstellen entsprechend den 4 möglichkeiten für cv-qualifikation (wobei die mit volatile niemals implizit deklariert werden). das wird zum beispiel bei std::auto_ptr genutzt (wohlgemerkt: die template-konstruktoren sind - selbst mit geeignetem templateparameter - keine copy-kosntruktoren).



  • Stimmt, der auto_ptr muss ja der rechten Seite den Pointer "entreissen" bei Zuweisung. Wieder was dazugelernt 😉

    Aber der Grund warum dieser CCtor trotzdem nicht gegriffen hat ist ja geklärt 😉



  • LordJaxom schrieb:

    Aber der Grund warum dieser CCtor trotzdem nicht gegriffen hat ist ja geklärt 😉

    Echt? Sry ich hab's noch nicht verstanden, wo war denn das Problem?

    mfg.



  • Wenn ich das richtig verstanden habe sind Referenzen auf temporary Objects immer konstant. Demnach kann

    CString s( CString::IntToHex(...) );
    

    Nur mit einem const CString& Ctor funktionieren. Wie von camper im anderen Thread zu dem Thema angesprochen, funktioniert hier eine Zuweisung, weil der Compiler daraus keine Initialisierung machen muss.

    CString s = CString::IntToHex(...);
    // ist hier also wirklich identisch zu
    CString s; s = CString::IntToHex(...);
    // weil keine geeignete Initialisierung verfügbar ist
    


  • Ist das dann wie folgt richtig?

    // CString.h
    int Length() const;
    
    // CString.cpp
    int CString::Length() const
    {
       return lstrlen(m_String);
    }
    

    Kommt mir etwas komisch vor mit dem const am Ende.


  • Mod

    LordJaxom schrieb:

    CString s = CString::IntToHex(...);
    // ist hier also wirklich identisch zu
    CString s; s = CString::IntToHex(...);
    // weil keine geeignete Initialisierung verfügbar ist
    

    nein. die erste variante ist immer eine initialisierung und benötigt einen geeigneten (und zugreifbaren) copy-konstruktor, selbst wenn dieser ctor-aufruf vom compiler eliminiert wird (das kreiden in letzterem falle nicht alle compiler an, also ggf. dem compiler geeignete switches für maximale konformität mitgeben). andere compiler haben diese furchtbare erweiterung, die ein binden von (non-const) rvalues an referenzen auf nicht-const erlauben. [anm.: erste zeile könnte noch andere konstruktoren involvieren, ein copy-ctor ist aber auf jeden fall erforderlich]

    die zweite zeile ist immer eine zuweisung. das muss nicht zwingend copy-zuweisung sein, falls ein geeigneter konvertierungspfad existiert, könnte hier auch ein anders überladener operator= aufgerufen werden.



  • Hö? Ich verstehe kein Wort. War das jetzt eine Antwort auf meine letzte Frage? Das mit dem Konstruktor hatten wir doch mittlerweile geklärt, oder?


  • Mod

    WebFritzi schrieb:

    Hö? Ich verstehe kein Wort. War das jetzt eine Antwort auf meine letzte Frage?

    nein, das war nicht die antwort auf deine frage. die wäre mit ja zu beantworten.

    Das mit dem Konstruktor hatten wir doch mittlerweile geklärt, oder?

    offenbar noch nicht ganz.



  • Ah... Vielen Dank für deine Antwort. 🙂


Anmelden zum Antworten