Guter Stil in C++



  • HumeSikkins schrieb:

    Wie löst man das Ganze effizient und so, dass selbst ein VB-Programmierer die Klasse sicher benutzen kann?

    Ich wette mit dir, du hättest fast Java-Programmierer geschrieben. 😉 🤡

    Shade Of Mine schrieb:

    Bedenke dass auch iteratoren nur Zeiger sind - niemand hindert einem an
    *vec.end();

    Bist du sicher? Ich bin eigentlich bisher immer davon ausgegangen, dass Iteratoren eines vectors gültig bleiben, wenn man pusht. Das würde aber implizieren, dass der Iterator kein Zeiger sein kann (zumindest nicht direkt auf das Element).
    Ich hoffe, ich täusche mich da jetzt nicht.



  • Bist du sicher? Ich bin eigentlich bisher immer davon ausgegangen, dass Iteratoren eines vectors gültig bleiben, wenn man pusht. Das würde aber implizieren, dass der Iterator kein Zeiger sein kann (zumindest nicht direkt auf das Element).
    Ich hoffe, ich täusche mich da jetzt nicht.

    schau dir den std::vector an, da werden die iteratoren beim pushen zum teil auch ungültig, weil ja immer komplett neuer speicher angefordert wird, und die iteratoren aus performancegründen einfache zeiger sind(acuh wenn man sie nicht als solche benutzen darf)



  • Wäre es nicht logischer, Iteratoren als Index zu implementieren?



  • nur im Falle der neuallokierung vom speicher, ansonsten währe es eher kontraproduktiv.

    im falle von erase, verlieren auch index-iteratoren ihre gültigkeit, weil sie zuweit zeigen(genauso wie pointer). Dazu sind sie auch noch langsamer ;).

    im falle von list-wo auch mit gekapselten pointern gearbeitet wird, wirds sogar noch deutlicher:

    wenn du in einer list 3 elemente hast,und würdest dann das 2. element löschen, würde es sich für einen pointer-iterator auf dem 3. element nicht auswirken. die position im speicher ändert sich für den node ja nicht.

    ein index operator würde aber gnadenlos ins leere stürzen, weil es keine performante möglichkeit gibt, seinen index mitzukorrigieren(die list hat ja nurnoch 2 elemente, und der index ist 3)



  • Das mit der list zählt jetzt aber nicht. Ich würde bestimmt nicht einen List-Operator als Index implementieren. Aber einen vector-Iterator evtl. schon.



  • Optimizer schrieb:

    Das mit der list zählt jetzt aber nicht. Ich würde bestimmt nicht einen List-Operator als Index implementieren. Aber einen vector-Iterator evtl. schon.

    Hindert dich ja niemand daran - zumindest fällt mir jetzt kein Grund ein, warum man es nicht so machen darf. Nur ist der Speicherverbrauch doppelt so hoch - weil du dir zusätzlich einen Zeiger auf den vector merken müsstest - das macht die Sache natürlich wieder lahm...

    Aber es ändert ja nichts an meiner Feststellung - denn ein vec[vec.size()] macht genauso *PENG*



  • otze schrieb:

    jeder kann sich mal im zeichen irren, es gibt sowas,d ass nennt sich flüchtigkeitsfehler^^

    Dann kannst du mir ja verraten, welche(s) Zeichen Flüchtigkeitsfehler waren. Meine Kristallkugel ist leider in der Reparatur. 🙄

    otze schrieb:

    wenn du mit tricksen pointer meisnt, dann sind das immernoch keine referenzen

    Nee, so hab ich das auch nicht gemeint. Hier nur mal so als schlechtes Beispiel:

    int& foo(int& x)
    {
        int* tmp = &x;
        ++tmp;
        return *tmp;
    }
    


  • Und findest du das jetzt guten Stil?



  • groovemaster schrieb:

    otze schrieb:

    jeder kann sich mal im zeichen irren, es gibt sowas,d ass nennt sich flüchtigkeitsfehler^^

    Dann kannst du mir ja verraten, welche(s) Zeichen Flüchtigkeitsfehler waren. Meine Kristallkugel ist leider in der Reparatur. 🙄

    da war ein & anstatt eines * sonst war der code korrekt.

    otze schrieb:

    wenn du mit tricksen pointer meisnt, dann sind das immernoch keine referenzen

    Nee, so hab ich das auch nicht gemeint. Hier nur mal so als schlechtes Beispiel:

    int& foo(int& x)
    {
        int* tmp = &x;
        ++tmp;
        return *tmp;
    }
    

    was meinste, was ich mit den pointern meinte? na? richtig, genau das, was du jetzt gemacht hast. Oi! :p



  • 0xdeadbeef schrieb:

    Den NULL-Wert kannst du an dieser Stelle eh nicht sinnvoll benutzen

    Ah. Wie schön das du das weißt ohne den Kontext zu kennen. Ich nutze hier den Nullwert zwar gerade völlig ohne Probleme, aber...

    Wenn du schon policy-basiert arbeitest, solltest du es auch richtig tun, und dann ist genau dieser Ansatz der einzig sinnvolle.

    Ah. Und wie es richtig ist, dass weißt nur du und das selbst wo du mein tatsächliches Problem gar nicht kennst.
    Genau so war das. Design ist eine crispe Wissenschaft und es gibt immer nur eine richtige Lösung. Hatte ich vergessen.
    Schade. Eigentlich hatte ich mir von meinem Posting mehr erhofft. Ein paar Ideen oder vielleicht ne Rückfrage.
    Über ein simples "du bist doof und so ist's richtig" bin ich ehrlich gesagt längst hinaus. Denn das ich doof ist mir nicht neu und das es nicht die eine richtige Lösung gibt, dass weiß ich nicht erst seit Fred Brooks' "No silver bullet".

    Im Endeffekt sähe das dann so aus: [...]

    Huh? Meine Kontext-Klasse muss den konkreten Typ der Policy kennen? Das gefällt mir aber ganz und garnicht.
    Ich sprach vom klassischen Strategy-Pattern. D.h. nicht, dass deine Lösung schlecht ist, sie passt nur nicht zu meinem Problem.

    Ansonsten bist du ja ständig am überprüfen if(foo_policy == NULL) default_stuff(); else foo_policy->stuff(); - ziemlich schlechter Stil imho.

    So schlimm ist es in meiner Situation gar nicht.
    Eher so:
    if (!filter.get() || (*filter)(entry))
    ...
    Kein else und kein doppelter Boden.
    Wichtig: In 97.24% der Fälle ist kein Filter nötig. In diesen 97.24% der Fälle spare ich mir so einen virtuellen Aufruf. Was, wie mir Messungen bestätigt haben, doch was bringt, da dieser Code sehr oft in einer Schleife aufgerufen wird. Noch besser wäre ein Template, da ich dann noch vom inlining profitieren könnte. Leider passt das aber nicht so gut zu meinen anderen Anforderungen.

    Ich wette mit dir, du hättest fast Java-Programmierer geschrieben.

    Die Wette hast du verloren. Ich habe einfach nur mit den Worten von Scott Meyers gesprochen. Aber schön das wir drüber geredet haben.



  • groovemaster schrieb:

    otze schrieb:

    jeder kann sich mal im zeichen irren, es gibt sowas,d ass nennt sich flüchtigkeitsfehler^^

    Dann kannst du mir ja verraten, welche(s) Zeichen Flüchtigkeitsfehler waren. Meine Kristallkugel ist leider in der Reparatur. 🙄

    Komisch, du korrigierst einen offensichtlichen Flüchtigkeitsfehler und sagst nachher die Kristallkugel ist in Reparatur? Des check i net!



  • @otze: du könntest z.B. boost::shared_ptr mit einem custom deleter verwenden: Policy 1 nimmt den Standard-Deleter, und policy 2 einen NULL-Deleter.

    Aber wahrscheinlich baust du mir jetzt schon was mit ner zirkulären Referenz 😉

    Nix gegen Pointer, oft genug ist es sinnvoll einen nackten Pointer einzusetzen, aber einen solchen zu verwenden wenn effiziente Alternativen vorhanden sind, halte ich für production code schon für schlecthen Stil.



  • HumeSikkins schrieb:

    Ah. Wie schön das du das weißt ohne den Kontext zu kennen. Ich nutze hier den Nullwert zwar gerade völlig ohne Probleme, aber...

    Nur, dass etwas funktioniert, heißt nicht, dass es auch guter Stil ist. Den Kontext hab ich mir halt so gut es ging hergereimt, zumal ich davon ausging, dass du nicht über ein konkretes Problem, sondern über policy-basiertes design allgemein sprichst - ich werd nen Teufel tun und hier ne Aura der Unfehlbarkeit anstreben.

    Den nächsten Absatz will ich nicht kommentieren, weil destruktiv.

    HumeSikkins schrieb:

    Huh? Meine Kontext-Klasse muss den konkreten Typ der Policy kennen? Das gefällt mir aber ganz und garnicht.

    Wo bitte hab ich das geschrieben? Du musst lediglich den Basistypen sowie eine default-policy kennen. Wenn du ne bessere Möglichkeit für runtime-policies kennst, sags mir.

    HumeSikkins schrieb:

    So schlimm ist es in meiner Situation gar nicht.
    Eher so:
    if (!filter.get() || (*filter)(entry))
    ...
    Kein else und kein doppelter Boden.

    Dass das legaler Code ist, wage ich gerade mal zu bezweifeln - es sei denn, du erwartest von deiner policy, dass sie einen *-Operator hat, und wenn du keine Pointer annimmst.

    Wenn filter ein pointer sein und das eigentlich filter->get() heißen soll, musst du NULL-Pointer vorher trotzdem abfangen, womit wir wieder bei unseren ifs wären.

    Soviel kann ich dazu sagen, ohne das Problem genauer zu kennen (wobei ich erst jetzt weiß, dass es um ein konkretes Problem geht). Erzähl mir mehr und ich kann dir was sinnvolleres sagen.



  • @ShadeOfMine:

    Sind Zeiger jetzt schlechter Stil?

    Nö, nur sollten sie meistens gekapselt sein.

    Ein Zeiger in C++ ist sehr ambivalent: Zeigt er auf ein Element oder viele? wieviele? Wer soll ihn wieder löschen? delete oder delete[] oder vielleicht MyAllocator::Delete() oder so ne schreckliche Microsoft-Funktion? Und wann genau wird er mir von jemand anderem unterm Hintern weggezogen?

    Wenn eine Factory-Klasse z.B. dokumentiert "you must call delete on the return value when no longer using it", ist das perfekt. Es sei denn, Du hast noch zwei anderer Factories mit anderen Policies. (Oder, noch viel schlimmer: neun Factories mit der gleichern Policy, und einen "Abweichler")

    Beispiel "mehrere Elemente": vector (Auch wenn ich mir dafür eine andere Schnittstelle wünschen würde)
    beispiel iterator: Der Zeiger ist gekapselt, und für iteratoren gelten immer feste regeln. Die sind zwar auch hinreichend kompliziert, aber immer noch weit unter einem Elementzeiger, und man sieht das es ein Iterator ist.



  • beispiel iterator: Der Zeiger ist gekapselt, und für iteratoren gelten immer feste regeln. Die sind zwar auch hinreichend kompliziert, aber immer noch weit unter einem Elementzeiger, und man sieht das es ein Iterator ist.

    mein std::vecor::iterator ist ein zeiger auf T. 😉

    @otze: du könntest z.B. boost::shared_ptr mit einem custom deleter verwenden: Policy 1 nimmt den Standard-Deleter, und policy 2 einen NULL-Deleter.

    ginge, aber ich bin eh immer kurz vorm pc zusammenbruch durch chronischen speichermangel*problem dramatisier*



  • MaSTaH schrieb:

    Komisch, du korrigierst einen offensichtlichen Flüchtigkeitsfehler und sagst nachher die Kristallkugel ist in Reparatur? Des check i net!

    Mir ging es keineswegs darum, kingruedi einen Fehler unter die Nase zu reiben. Ich wollte einfach nur wissen, was richtig ist. Und da es mehrere Korrekturmöglichkeiten gibt, war ich mir nicht sicher. Aber offensichtlich haben hier einige Leute mächtig Spass daran, auf solchen Dingen rumzureiten. Wenn's für dich so offensichtlich ist, wieso hast du's nicht denen, für die es das nicht ist, einfach mitgeteilt? Und wenn du dir die Beiträge mal genauer durchgelesen hättest, hättest du gemerkt, dass der Codeschnipsel jetzt eh nicht mehr von Bedeutung ist, da die Absicht der Aussage geklärt ist (denk ich zumindest).

    Optimizer schrieb:

    Und findest du das jetzt guten Stil?

    groovemaster schrieb:

    Hier nur mal so als schlechtes Beispiel

    Wer lesen kann, ist klar im Vorteil. 😉

    otze schrieb:

    was meinste, was ich mit den pointern meinte? na? richtig, genau das, was du jetzt gemacht hast.

    Du hast aber mit deiner Aussage impliziert, dass jemand Zeiger mit Referenzen gleichsetzt. Und das ist in dem Codebeispiel nicht der Fall.



  • Nein hat er nicht. Vielleicht solltest du selber mal anfangen, besser zu lesen. Die Frage ist, ob das sinnvoll ist, was du da mit den Referenzen gemacht hast.
    Was du wiederum als Antwort darauf gepostet hast, dass man Referenzen nicht löschen kann. Was zwar natürlich so nicht stimmt, aber man macht es trotzdem nicht. In so einem Fall ist ein Zeiger angebrachter, weil ein Zeiger gemeint ist, wie es volkard so schön gesagt hat. Dein Code hat den Eindruck erweckt, es ist super, zeiger hinter referenzen zu verstecken und dann auf umwege doch wieder das Objekt zu deleten.
    Aber das ist kein guter Stil. Ob du jetzt diese Ansicht vertrittst oder nicht, ist mir nicht klar geworden und mir auch ziemlich egal. Aber dein Code mit delete &myReference ist auf jeden Fall kritikwürdig und um nichts anderes geht es.



  • groovemaster schrieb:

    Aber offensichtlich haben hier einige Leute mächtig Spass daran, auf solchen Dingen rumzureiten. Wenn's für dich so offensichtlich ist, wieso hast du's nicht denen, für die es das nicht ist, einfach mitgeteilt?

    Schon gut, reg dich ab... Wir haben aneinander vorbei geredet.



  • Optimizer schrieb:

    Nein hat er nicht.

    Und wie liest du dann

    otze schrieb:

    wenn du mit tricksen pointer meisnt, dann sind das immernoch keine referenzen

    ?

    Optimizer schrieb:

    Die Frage ist, ob das sinnvoll ist, was du da mit den Referenzen gemacht hast.

    Und die Antwort darauf hab ich ja auch schon gegeben. Aber für dich sag ich es nochmal ganz ausführlich: N-E-I-N

    Optimizer schrieb:

    Was du wiederum als Antwort darauf gepostet hast, dass man Referenzen nicht löschen kann.

    Nee, ich hab das als Antwort auf

    otze schrieb:

    referenzen können nicht auf den nächsten speicherbereich inkrementiert werden

    gegeben, und dachte eigentlich, dass das aus dem Beitrag hervorgeht.

    Optimizer schrieb:

    Aber dein Code mit delete &myReference

    könntest du mir mal auf die Sprünge helfen, wusste gar nicht, dass ich sowas gepostet hab...



  • Gut, es ging ums inkrementieren, nicht ums löschen. Ich hab da nicht extra noch mal nachgesehen.

    Und die Antwort darauf hab ich ja auch schon gegeben. Aber für dich sag ich es nochmal ganz ausführlich: N-E-I-N

    So deutlich war das IMHO nicht.
    "Hier nur mal so als schlechtes Beispiel" kann auch einfach heißen, dass nur dieses Beispiel schlecht ist und sonst die Vorgehensweise gut ist.

    Und wie liest du dann [...]

    So, dass otze meint, dass du immer noch insgesamt keine Referenz verwendet hast, sondern nur hinter einem Pointer versteckt und insgeheim doch wieder für alles die Adresse nimmst.
    Du hast also nicht "mit Tricks" eine Referenz inkrementiert sondern den Pointer. Die Referenz wird dadurch nicht verändert.


Anmelden zum Antworten