kapseln einer Funktion führt zu eigenartigen Fehlern



  • Ich habe einen Klasse (siehe Code) geschrieben welche Vergleichsfunktionen für eine Templateklasse zur Verfügung stellt (eine Art generisches Array).

    So eingebunden funktioniert alles wunderbar! Allerdings wollte ich nun die Funktion "tolower" abkapseln (sofern man das so nennt... jedenfalls wollte ich sie nicht mehr aus dem restlichen Code aufrufbar machen) und habe die Funktion in den "private:" Bereich der Klasse gesteckt. Von da an gabs nur mehr wilde Fehler 😞

    #include <ctype.h>
    
    string tolower(const string &str) {
    	string result = str;
    	for (int i = 0; i < int(result.size()); ++i)
    		result[i] = tolower(result[i]);
    	return result;
    }
    
    class StringCompare {
    public:
    	bool less(const string &a, const string &b) {
    		return tolower(a) < tolower(b);}
    	bool equal(const string &a, const string &b) {
    		return tolower(a) == tolower(b);}
    	bool greater(const string &a, const string &b) {
    		return tolower(a) > tolower(b);}
    
    };
    

    Konkret fängt der Compiler an folgendes zu bemängeln:
    - invalid conversion from 'char' to 'const char*'
    - cannot convert 'std::string' to 'char' in assignment

    Jetzt könnte ich mich damit zu frieden geben, dass es ausgelagert seinen Dienst tut, allerdings würde ich das wirklich gerne verstehen (möchte nicht ewig ein Anfänger bleiben).

    Danke im Voraus für Tipps und Anregungen!



  • Lad' Dir am besten mal einen Compiler runter, der dir mit der Fehlermeldung noch die Zeile ausspuckt!



  • result[i] = tolower(result[i]);
    

    er zerlegt sich in dieser Zeile... also was hier Zeile 6 war, denn Probleme gibt es ja erst wenn man die Funktion in den Bereich der Klasse verschiebt.



  • Aus irgend einem Grund findet er tolower aus ctype.h nicht.

    Daher will er dein tolower nehmen, und da passt weder der Parameter-Typ noch der Return-Typ. Und daher motzt er.



  • Ach herrjeh!
    Du hast tolower (sinnloser Weise) in die Klasse rein verschoben?
    Dann ist klar dass er die globale Funktion gleichen Namens nimmer findet.
    Du kannst explizit qualifizieren wenn du magst, also ::tolower schreiben, dann findet er sie wieder.



  • zuerst einmal DANKE für die schnelle Hilfe bzw. Erklärung!

    hustbaer schrieb:

    Ach herrjeh!
    Du hast tolower (sinnloser Weise) in die Klasse rein verschoben?

    Und ja, ich bin noch ein rechter Beginner und dachte, wenn eine Funktion _nur_ von einer Klasse benutzt wird, sie eben auch dort reingesteckt werden sollte.



  • Brusik schrieb:

    Und ja, ich bin noch ein rechter Beginner und dachte, wenn eine Funktion _nur_ von einer Klasse benutzt wird, sie eben auch dort reingesteckt werden sollte.

    Naja, darüber kann man jetzt streiten. Ich persönlich stecke nichts in Klassen rein, was nicht in Klassen sein muss.

    D.h. ich versuche das Interface minimal zu halten.

    Auch wenn es um private Funktionen einer Klasse geht, versuche ich da nichts rein zu tun was nicht da sein muss. D.h. wenn die Funktion 1:1 ohne Änderung auch als freie Funktion läuft (wie in deinem Fall), dann mache ich eine freie Funktion.
    Deine Funktion ist sehr allgemein, d.h. es spricht eigentlich auch nichts dagegen wenn sie überall verfügbar ist, komplett unabhängig von der Klasse.

    Manche Funktionen sind allerdings - trotz dem sie problemlos als freie Funktion implementierbar sind - sehr eng mit einer Klasse verbunden.
    z.B. Helper-Funktionen, die man braucht um die Klasse zu implementieren. Dort muss man oft Dinge auf eine ganz bestimmte Art und Weise machen, die zur Implementierung der Klasse "passt". Solche Funktionen öffentlich zu machen ist IMO eine schlechte Idee, da man sie nicht "stabil" halten kann/möchte. Bei Änderungen an der Implementierung der Klasse könnte es sein dass man diese Funktionen anpassen muss, so dass sie anders arbeiten als vorher. Wenn die Helper-Funktionen dann aber öffentlich sind, kann (bzw. soll) man nicht einfach ihre Bedeutung ändern, das könnte zu Problemen führen. Bzw. man müsste alle Stellen checken, wo sie irgend jemand verwendet hat.

    Bzw. es gibt auch diverse Helper-Funktionen die "ausserhalb" einer Klasse bzw. eines bestimmten Moduls einfach überhaupt keinen Sinn machen. Konstruiertes Beispiel, weil mir gerade nix besseres einfällt: man könnte irgendwo u.U., um Code-Duplizierung zu vermeiden, eine Funktion brauchen, die "ende" in eine Datei schreibt (an den bestehenden Inhalt anhängt), die Datei dann "flusht", und sie dann schliesst. Eine öffentliche Funktion zu machen, die genau das tut, wäre aber ziemlich sinnlos - die Chancen dass man genau das noch an anderer Stelle braucht sind minimal. Man würde sich also bloss den Namespace mit einem unnützen Funktionsnamen zumüllen.

    Einfache Lösung: man definiert solche Funktionen in einem unnamed Namespace, direkt IN dem .cpp File wo man sie verwendet. Dadurch kann die Funktion nicht von ausserhalb dieses Files aufgerufen werden, bzw. sogar noch besser: sie ist von ausserhalb nichtmal wirklich "sichtbar".
    (Für die Haarspalter: ich weiss dass solche Funktionen trotzdem "external linkage" haben, DAS meine ich nicht mit "sichtbar")

    Sollte die Funktion doch in mehr als nur einem .cpp File gebraucht werden, aber dennoch zu "spezifisch" sein als dass man sie vollkommen "öffentlich" machen möchte, dann kann man sie in einem "Detail" Namespace deklarieren. Dadurch ist sie zwar nicht mehr total "unsichtbar" für andere Programmteile, aber sie müllt wenigstens nicht mehr den "nicht-Detail" Namespace zu. Weiterer Bonus: wenn jmd. "Detail::Irgendwas()" schreiben muss, um eine Funktion aufzurufen, sollte ihm klar sein, dass er hier auf ein Implementierungs-Detail von etwas zugreift, und nicht auf einen "offiziellen" Teil einer Library/eines Moduls/...
    Tut er es trotzdem, dann kann man wenigstens mit ruhigem Gewissen sagen dass er selbst schuld ist, wenn die Funktion mal entfernt wird oder einfach was ganz anderes macht als zuvor.

    ----

    Ein IMO sehr empfehlenswerter Artikel der hier - mehr oder weniger - dazupasst: What's In a Class? - The Interface Principle



  • hustbaer schrieb:

    Einfache Lösung: man definiert solche Funktionen in einem unnamed Namespace, direkt IN dem .cpp File wo man sie verwendet. Dadurch kann die Funktion nicht von ausserhalb dieses Files aufgerufen werden, bzw. sogar noch besser: sie ist von ausserhalb nichtmal wirklich "sichtbar".

    Und man hat nicht die Probleme mit den scheiß Headern.



  • wow, tausendmal Danke für die wirklich informative Antwort! werde den guide studieren und gelobe Besserung 🙂


Anmelden zum Antworten