Problem mit const-Objekt und Rückgabewert einer Memberfunktion



  • Hallo,

    mir ist nicht ganz klar, wie ich dieses gelöst bekomme: ich will eine Funktion eines als const definierten Objekts aufrufen, die mir einen Wert zurückgibt. Dabei stoße ich auf unerwartete Schwierigkeiten...

    Hier erstmal mein Code:

    // Deklaration
    void CHTMLParser::FetchRobotsTxt(const CDownload &p_CDownload, CCacheURL &p_CCacheURL);
    
    // Definition
    void CHTMLParser::FetchRobotsTxt(const CDownload &p_CDownload, CCacheURL &p_CCacheURL) 
    {
      switch (p_CDownload.DownloadRobots(...)) {  // hier meckert der Compiler (s.u.)
        ...
      }
    
    // Deklaration
    const int CDownload::DownloadRobots(const char *p_pcURL, char *p_pcBody, int &p_iDuration, int &p_iHTTPStatusCode);
    
    // Definition
    const int CDownload::DownloadRobots(...) {
      int l_iRet = 0;
    
      ...
    
      l_iRet = 12;
      ...
    
      return l_iRet;
    }
    

    Die Meldung des Compiler ist relativ eindeutig:

    'CDownload::DownloadRobots' : cannot convert 'this' pointer from 'const CDownload' to 'CDownload &'

    Ich habe schon diverse andere Sachen 'durchprobiert' - leider ohne Erfolg. Kann mir jemand sagen wo der Fehler liegt (und wie ich ihn gelöst bekomme)?!

    Gruß,
    Sid73



  • Du rufst eine non-const Methode CDownload::DownloadRobots auf ein const Object in CHTMLParser::FetchRobotsTxt auf. Eine Loesung koennte sein, die Methode CDownload::DownloadRobots als const zu deklarieren. Welche Auswirkungen das auf den Rest des Codes hat, kann ich aber nicht sagen.

    Frage: Wofuer steht das p_ in p_CDownload bei CHTMLParser::FetchRobotsTxt?



  • knivil schrieb:

    Frage: Wofuer steht das p_ in p_CDownload bei CHTMLParser::FetchRobotsTxt?

    Eine wage Deutung: das p_ steht für Parameter :p
    Edit: sowie l_ für Lokal 😉

    Masochismus vom feinsten 😃



  • knivil schrieb:

    Du rufst eine non-const Methode CDownload::DownloadRobots auf ein const Object in CHTMLParser::FetchRobotsTxt auf. Eine Loesung koennte sein, die Methode CDownload::DownloadRobots als const zu deklarieren. Welche Auswirkungen das auf den Rest des Codes hat, kann ich aber nicht sagen.

    In CDownload::DownloadRobots ändere ich tatsächlich auch Membervariablen (hätte ich wohl mal erwähnen sollen...). Ich schleife das Objekt seit 3 Aufrufen mit durch und wollte mit const verhindern, das es in den anderen Funktionen geändert werden kann. Letztendlich wollte ich es aber _hier_ (in DownloadRobots) dann doch ändern.

    const_cast hatte ich mir auch schon angesehen

    int i = const_cast<int>(p_CDownload.DownloadRobots(...));
    

    ergibt:

    error C2440: 'const_cast' : cannot convert from 'int' to 'int'
    Conversion is a valid standard conversion, which can be performed implicitly or by use of static_cast, C-style cast or function-style cast

    und bei

    CDownload* test = const_cast<CDownload *>(p_CDownload);
    

    will er einen selbstdefinierten Operator haben:

    error C2440: 'const_cast' : cannot convert from 'const CDownload' to 'CDownload *'
    Conversion requires a constructor or user-defined-conversion operator, which can't be used by const_cast or reinterpret_cast

    Wozu eigentlich? Ich hatte das so verstanden, dass man einen Pointer auf das ursprüngliche Objekt zurückbekommt.

    Frage: Wofuer steht das p_ in p_CDownload bei CHTMLParser::FetchRobotsTxt?

    Pointer



  • [quote="evilissimo"]

    knivil schrieb:

    Frage: Wofuer steht das p_ in p_CDownload bei CHTMLParser::FetchRobotsTxt?

    Eine wage Deutung: das p_ steht für Parameter :p

    Nicht ganz....

    Eine wage Deutung: das p_ steht für Parameter :p
    Edit: sowie l_ für Lokal 😉

    Immerhin eine richtige Antwort... 😉

    Masochismus vom feinsten 😃

    Wie beziechnest Du denn lokale Variablen?



  • Der Hund heißt Hund, die Katze heißt Katze und das Kind heißt Kind.



  • Sid73 schrieb:

    Wie beziechnest Du denn lokale Variablen?

    Ich nehme mal an ohne Präfixe (Ich persönlich schreibe sowohl Membervariablen, Parameter als auch lokale Variablen allesamt in pascalCase-Notation ohne Präfixe). Da ich zudem versuche Funktionen/Methoden klein zu halten sehe ich darin auch kein Problem (Und wenn nötig setze ich bei einer Membervariable ein this-> davor).



  • CDownload* test = const_cast<CDownload *>(p_CDownload);
    

    p_CDownload ist aber eine Referenz und kein Zeiger. Sehr irrefuehrend wenn p_ fuer Pointer steht.

    Warum soll eigentlich p_CDownload in CHTMLParser::FetchRobotsTxt als const uebergeben werden, wenn es in der Methode veraendert wird, also nicht const ist? Das widerspricht der Logik.



  • Sid73 schrieb:

    ...
    In CDownload::DownloadRobots ändere ich tatsächlich auch Membervariablen...wollte mit const verhindern, das es in den anderen Funktionen geändert werden kann...

    Also das ist irgendwie ein Widerspruch, der auch über mehrere Aufrufebenene bestehen bleibt.
    Wenn ich der "erste Ebene" ein const& übergebe, erwarte ich wirklich, dass das Objekt unverändert ... egal, welche Funktionen diese intern nutzt (was ich ja meistens nicht einmal sehen kann).

    const&-Parameter sind eine Zusage an den Aufrufer!!
    Wenn Du const als "Programmierhilfe" nutzen möchtest (was ja durchaus legitim ist), kannst Du immer noch lokale const& anlegen:

    void f(int& i) { // f darf i ändern
       {  // hier will ich i nicht ändern
          int const& l_i;
          ... mach was mit l_i ...
       }
       g(i); // weitergeben
    ...
    

    Du solltest nicht vergessen, dass man const&-Parametern eine ganze Menge übergeben kann, was man wirklich nicht (sicher) verändern kann!
    Deshalb ist der const_cast-Hack auch nicht wirklich der Bringer ...

    Gruß,

    Simon2.



  • knivil schrieb:

    Warum soll eigentlich p_CDownload in CHTMLParser::FetchRobotsTxt als const uebergeben werden, wenn es in der Methode veraendert wird, also nicht const ist? Das widerspricht der Logik.

    Weil p_CDownload ja eigentlich nur eine Memberfunktion aufruft, die mir ein int zurückgibt. Mein Gedanke war 'Ok, FetchRobotsTxt ändert nichts an p_CDownload, also kann ich das als const definieren.' - offensichtlich ein falscher Gedanke.

    Das mit den Präfixen werd' ich mal mit aufnehmen...



  • Simon2 schrieb:

    Wenn Du const als "Programmierhilfe" nutzen möchtest (was ja durchaus legitim ist), kannst Du immer noch lokale const& anlegen:

    void f(int& i) { // f darf i ändern
       {  // hier will ich i nicht ändern
          int const& l_i;
          ... mach was mit l_i ...
       }
       g(i); // weitergeben
    ...
    

    Mir ist nicht ganz klar, wie in dem Beispiel verhindert wird, das f() i ändern kann. Mit den Klammern wird doch nur verhindert, dass ich l_i außerhalb des Scopes ändern kann, oder?

    Mal für mich zum prinzipiellen Verständis: const kann ich bei Referenzen also nur verwenden, wenn das Objekt in der Funktion nicht verändert wird, z.B. bei Gettern. Bei Aufrufen über mehrere Ebenen hinweg, muß ich selbst drauf achten, dass in den oberen Ebenen da nichts schief läuft?



  • Sid73 schrieb:

    ...
    Mir ist nicht ganz klar, wie in dem Beispiel verhindert wird, das f() i ändern kann. ...

    Gar nicht.

    Das entspricht ja (vermutlich/anscheinend) auch nicht Deiner Intention . Du WILLST ja die übergebene Variable ändern ... aber eben nur an einer bestimmten Stellen (wo Du sie einer anderen Funktion übergibt).

    Sid73 schrieb:

    ...
    Mit den Klammern wird doch nur verhindert, dass ich l_i außerhalb des Scopes ändern kann, oder?...

    Ich vermute, Du meinst das richtige und hast es nur falsch geschrieben:
    Richtig wäre: "Mit den Klammern wird doch nur verhindert, dass ich l_i (genauer: "i über l_i") innerhalb des Scopes ändern kann, ..."

    Natürlich ist es auch innerhalb des Scopes möglich, direkt und verändernd auf i zuzugreifen ... aber ich hatte die Intention so verstanden, dass es darum geht, sicherzustellen, dass innerhalb von f() nur an bestimmten Stellen (nämlich beim Aufruf einer anderen Funktion) i verändert werden kann.

    Mehr so als "Programmierhilfe", damit man nicht über sowas stolpert:

    void f(int& i) {
       .... 100 Zeilen Code ...
       if(i = 0) {
        ....
       }
       ... 200 Zeilen Code ...
       g(i); // nur hier will ich i verändern
    ...
    

    Sid73 schrieb:

    ...const kann ich bei Referenzen also nur verwenden, wenn das Objekt in der Funktion nicht verändert wird, ...

    ja und nein ...

    Ich würde sagen: Mit "const" teile ich mit, dass ich das Objekt über diese Variable nicht ändern will. (geht mit (auto-)Objekten, Referenzen und Zeigern)
    Der Compiler prüft dann, ob für const-Variablen auch wirklich nur const-Funktionen aufgerufen werden.
    Das betrifft beileibe nicht nur "Getter", sondern eigentlich eine Vielzahl von Funktionen (Beispiel, wo i const sein kann):
    - Ausgabefunktionen ( cout << i; )
    - Wo aus einem Parameter ein anderer Wert berechnet wird ( ... = sqrt(i); )
    - Wo der Zustand eines anderen Objektes gesetzt wird ( x.setVal(i); )
    - ...
    im letzten Beispiel taucht const sogar in einem Setter auf... 😉
    setVal() und ein mögliches Pendant getVal() hätten dann eine Deklaration wie

    struct X {
       void setVal(int const& i);
       int getVal() const;
    ...
    

    Was bedeutet:
    - setVal() verändert i nicht, aber das X-Objekt, für das es aufgerufen wird
    - getVal() verändert das X-Objekt, für das es aufgerufen wird, nicht.

    Ich könnte mir sogar vorstellen, dass const bei Funktionsparametern und Memberfunktionen der Default ist und man stattdessen solche Parameter/Memberfunktionen kennzeichnet, die das Objekt verändern ...

    @"mehrere Ebenen": Ja, da musst Du selbst darauf achten. Du kannst halt Programmiertricks wie den von mir genannten nutzen, aber letztlich ist Dir das selbst überlassen.
    Wechsele mal die Perspektive: Demjenige, der Deine Funktion nutzt ist egal (und oft nichtmal einsehbar), wie Du Deine Funktion implementierst (Du könntest auch das, was die "innere Funktion" tut, auch direkt implementieren!)

    Für ihn ist lediglich wichtig, ob Du das Objekt veränderst oder nicht.
    ... und da Du das tust (immerhin hat in Deinem Code das Objekt dann einen "DownloadRobots()"-Call hinter sich), ist es nur konsequent, wenn Du das dem Aufrufer mitteilst.

    Gruß,

    Simon2.



  • Vielen Dank! Habe es jetzt zumindest besser verstanden. 🙂


Anmelden zum Antworten