+ Operator überladen funktioniert nicht richtig



  • Mal abgesehen davon, dass solche Operatoren zu unerwarteten Ergebnissen führen können (...)

    In wie fern denn? Was genau könnte denn die Folge dabei sein bzw. wie tritt da denn ein unerwartetes Verhalten auf? (Speicherfehler und Programmabsturz oder sowas?)



  • Ynnus schrieb:

    Mal abgesehen davon, dass solche Operatoren zu unerwarteten Ergebnissen führen können (...)

    In wie fern denn? Was genau könnte denn die Folge dabei sein bzw. wie tritt da denn ein unerwartetes Verhalten auf? (Speicherfehler und Programmabsturz oder sowas?)

    Du kannst nicht kontrollieren, was das aufrufende Programm mit deinem Pufferspeicher anstellt - wenn da etwas "falsches" gemacht wird, bekommst du Speicherfehler (oder schlimmeres).

    string_cls str("Hallo");
    char* cstr=str;// hier greift der Umwandlungsoperator
    delete[] cstr;// der Puffer wird gelöscht - und spätestens beim str-Destruktor kracht es
    


  • Oha, ok, danke. Das wär' natürlich nicht so wunderbar. Was könnte man dagegen tun? Wenn ich im Dekonstruktor aber den Speicher des Pointers nur bedingt freigebe, wenn er denn tatsächlich auch noch existiert (!NULL ist), könnte man damit den Crash verhindern? Oder wird der pointer nicht NULL wenn ein anderer Pointer diese Speicherstelle freigibt? (es muss sich doch erkennen lassen, ob der Speicher noch besteht oder nicht...)



  • In dem Beispiel oben wird der member-Pointer nicht geändert (du arbeitest mit einer Kopie, also dürfte eine Überprüfung auf NULL nicht viel bringen. Und ein Pointer kann leider nicht überprüfen, ob jemand anderes den auf ihn verwiesenen Speicherplatz freigegeben hat.

    Am sichersten verhinderst du so einen Crash, indem du die Übergabe als char* komplett verhinderst (das mindeste ist, den Cast-Op in "operator const char*()" umzudefinieren, besser ist eine explizite Umwandlungsfunktion für diesen Zweck (ala string::c_str())).



  • Nein, das lässt sich leider nicht ohne weiteres erkennen. Wenn du einen Pointer auf einen Speicherbereich deletest, wird er nicht auf NULL gesetzt - und andere Pointer auf denselben Speicherbereich schon garnicht.



  • Ynnus schrieb:

    In wie fern denn? Was genau könnte denn die Folge dabei sein bzw. wie tritt da denn ein unerwartetes Verhalten auf? (Speicherfehler und Programmabsturz oder sowas?)

    Nein, ich meinte mit "unerwarteten Ergebnissen" eher, dass der Compiler zB dann eine solche implizite Umwandlung durchführt, wenn du das vielleicht gar nicht möchtest. Oder dass es zu nicht auflösbaren Mehrdeutigkeiten kommen kann. Das soll nicht heissen, dass du den Operator nicht verwenden sollst. Du musst aber noch mehr aufpassen, was du machst.

    Speicherfehler und Programmabsturz können entstehen, wenn jemand den Speicher hinter diesem rohen Zeiger manipuliert. Den Einwand von CStoll und dem delete[] vergisst du am besten schnell wieder, sowas kannst du nicht verhindern. Wer sowas macht, sollte einfach bestraft werden, imo. Wie gesagt, für den Anfang mach die Funktion erstmal const.

    string_cls::operator const char*() const
    

    Das hilft schon mal. Besser wäre es natürlich, wenn der zurückgegebene Zeiger auch noch const wäre, denn Lesen kann ja auch schon zu undefiniertem Verhalten führen. Das ist in der Praxis aber einfach zu unhandlich.
    Wenn jemand den String verändern will, dann biete op[] dafür an. Dort kannst du dann prüfen, ob der Anwender korrekt zugreift.



  • groovemaster schrieb:

    Den Einwand von CStoll und dem delete[] vergisst du am besten schnell wieder, sowas kannst du nicht verhindern. Wer sowas macht, sollte einfach bestraft werden, imo. Wie gesagt, für den Anfang mach die Funktion erstmal const.

    Das war auch nur ein Extrembeispiel. Und man kann solche Mainpulationen auf jeden Fall stark erschweren, indem man keine Zeiger (oder Referenzen) auf die lokalen Daten rausgibt - aber da mußt du abwägen, inwieweit es noch sinnvoll ist.



  • Wenn jemand den String verändern will, dann biete op[] dafür an. Dort kannst du dann prüfen, ob der Anwender korrekt zugreift.

    Wie überprüfe ich denn die korrekte Handhabung mit dem Pointer? Ich brauche den vor Allem auch, um ihn an diverse WinAPI-Funktionen als Buffer zu übergeben. Und da ist eben das Problem, dass diese Funktionen dort hinein schreiben und ich nicht sehe was genau die machen. Wie soll man da überprüfen, dass mit dem Pointer korrekt gehandhabt wurde?



  • Ynnus schrieb:

    Wie überprüfe ich denn die korrekte Handhabung mit dem Pointer? Ich brauche den vor Allem auch, um ihn an diverse WinAPI-Funktionen als Buffer zu übergeben. Und da ist eben das Problem, dass diese Funktionen dort hinein schreiben und ich nicht sehe was genau die machen. Wie soll man da überprüfen, dass mit dem Pointer korrekt gehandhabt wurde?

    So 'ne richtig zufrieden stellende Lösung hab ich dafür auch noch nicht gefunden. Ich mach es idR so, dass ich der Klasse eine resize() Funktion spendiere. Vor dem Aufruf der WinAPI Funktion, rufe ich dann resize() mit einer entsprechenden Grösse auf, um der Sequenz (in deinem Fall ein String) eine entsprechende Mindestlänge zu verpassen. Der WinAPI Funktion übergebe ich dann den Anfang des internen Puffers, zB &foo[0] oder &foo.front(), und die Länge (foo.length()), damit sie weiss, wieviel sie maximal reinschreiben darf. Nach dem Aufruf korrigiere ich die Länge wieder mit resize(), je nachdem, wieviel die WinAPI Funktion reingeschrieben hat.



  • Eine set_size(int size) Funktion hab ich auch schon drinne jetzt. Bei den WinAPI Funktionen muss man ja sowieso immer die Größe des Buffers angeben, demnach setze ich vorher die Größe, übergebe den pointer und teile die maximale Länge mit. Also so wie du das vorgeschlagen hast, im Grunde genommen.


Anmelden zum Antworten