Autor Thema: C oder C++  (Gelesen 23487 mal)

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 17. February 2012, 18:28 »
Damit ich daran erinnert werde, dass hier was passiert. Dass ich auf einen Blick die möglichen Codepfade sehen kann. Ohne den Code zu kennen und ohne zu wissen, welche anderen Klassen Destruktoren haben.
Ich weiß, meiner Fragerei ist unglaublich nervig, aber es will mir nicht einleuchten. Warum willst du daran erinnert werden, dass da was passiert? Warum willst du auf einen Blick alle möglichen Codepfade sehen?
Du musst nicht wissen, welche anderen Klassen Destruktoren haben.

Hab mal ein bisschen damit rumgespielt (verzeih mir mein C++, ich würde das normal nicht tun):
[...]
Und was soll das jetzt aussagen, außer dass der Programmierer (= du) einen Fehler gemacht hat? Davor kann dich keine Sprache beschützen.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 17. February 2012, 18:39 »
Ich weiß, meiner Fragerei ist unglaublich nervig, aber es will mir nicht einleuchten. Warum willst du daran erinnert werden, dass da was passiert? Warum willst du auf einen Blick alle möglichen Codepfade sehen?
Du musst nicht wissen, welche anderen Klassen Destruktoren haben.
Damit ich verstehe, was passiert, und nicht Code produziere, der nicht tut, weil die Destruktoren, an die ich nicht denken soll, irgendwelche Nebeneffekte haben?

Zitat
Und was soll das jetzt aussagen, außer dass der Programmierer (= du) einen Fehler gemacht hat? Davor kann dich keine Sprache beschützen.
Die Sprache hat mir gesagt: "Kümmer dich nicht um das Aufräumen, ich mach das". Sie hat damit erstens dafür gesorgt, dass ich nicht drauf aufmerksam werde, weil es implizit passiert, und mir zweitens die Möglichkeit weggenommen, selber in der richtigen Reihenfolge aufzuräumen. Andersrum aufräumen hätte in diesem Beispiel nämlich funktioniert.

Du hast gesagt "Indem man die Objekte umgekehrt zu ihrer Erzeugungsreihenfolge zerstört, kann eben nichts schief gehen." und plötzlich geht eben doch was schief, weil die Sprache meint, schlauer zu sein als der Programmierer.

Nur aus Neugier, wie würde man das richtig machen, wenn man solche Beziehungen hätte? Muss ich die Beziehung erst von Hand brechen bevor ich die Destruktoren laufen lassen kann?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Paprikachu

  • Beiträge: 6
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 17. February 2012, 19:21 »
Damit ich verstehe, was passiert, und nicht Code produziere, der nicht tut, weil die Destruktoren, an die ich nicht denken soll, irgendwelche Nebeneffekte haben?
Destruktoren haben üblicherweise keine Nebeneffekte, an die du denken sollst. In keiner gut designten Lib gibt es Dtoren, bei denen du dich um irgendwas kümmern musst.

Die Sprache hat mir gesagt: "Kümmer dich nicht um das Aufräumen, ich mach das". Sie hat damit erstens dafür gesorgt, dass ich nicht drauf aufmerksam werde, weil es implizit passiert, und mir zweitens die Möglichkeit weggenommen, selber in der richtigen Reihenfolge aufzuräumen. Andersrum aufräumen hätte in diesem Beispiel nämlich funktioniert.

Du hast gesagt "Indem man die Objekte umgekehrt zu ihrer Erzeugungsreihenfolge zerstört, kann eben nichts schief gehen." und plötzlich geht eben doch was schief, weil die Sprache meint, schlauer zu sein als der Programmierer.

Nur aus Neugier, wie würde man das richtig machen, wenn man solche Beziehungen hätte? Muss ich die Beziehung erst von Hand brechen bevor ich die Destruktoren laufen lassen kann?
Ach, jetzt verstehe ich das Problem erst. Ziemlich verzwickte Sache. Lösung: Überdenk dein Design, ich glaube nicht, dass das so irgendwie Sinn ergeben würde.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 17. February 2012, 19:35 »
Das sieht vielleicht konstruiert aus, aber ich glaube nicht, dass das abwegig ist, was ich mache. Ich habe zwei Objekte A und B auf dem Stack und ein Attribute von A  fängt irgendwann an, auf B zu verweisen. Ich vermute, das ist schon genug, damit bei der falschen Reihenfolge das Verhalten laut Standard undefiniert wird (nicht nachgelesen allerdings); in der Praxis hat es nicht gereicht, weil B - obwohl destruiert - immer noch auf dem Stack liegt. Deswegen habe ich in B intern noch ein Objekt C auf dem Heap eingeführt, das mit dem Destruktor definitiv ungültig wird.

Ich finde das alles ganz und gar nicht abwegig. C ist ein Implementierungsdetail von B. Der entscheidende Fehler, den das Programm macht, ist, dass es einem früher definierten Objekt ein später definiertes als Attribut zuweist. Dass das Vertauschen von Definitionen, die (bei der Initialisierung) nicht voneinander abhängen, ein Programm fixt, kann man logisch finden, wenn man unbedingt will. Muss man aber sicher nicht.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Sannaj

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #24 am: 17. February 2012, 20:30 »
Ein weiterer Schwachpunkt von C ist das Fehlen von überladbaren Methoden, das bei der Lowlevelprogrammierung aber auch nicht so zu Geltung kommt. (Der Kernel ist ja keine Mathesoftware, die generische sin(), cos() etc. Funktionen braucht.).
Mit _Generic geht das wohl in C11 so einigermaßen. Obwohl ich jetzt einfach mal sage, einen Schönheitswettbewerb gewinnt das nicht.

Naja, _Generic ist schon in Ordnung. Man kann ja #define generic _Generic oder so nutzten.
Zitat
Womit auch das Stichwort für das eine wirklich fehlende Feature in C gegeben ist: Richtige Generics. Also der Teil von Templates, den man wirklich braucht. Auf das Berechnung von Primzahlen zur Compilezeit verzichte ich gern. ;)
Generische Funktionen ließen sich sehr gut umsetzten, wenn man eine komfortable Makrofunktion einbaut. Allerdings nützten dir generische Funktion in C nicht so viel, weil du keine Operatoren überladen kannst.

LittleFox

  • Beiträge: 306
    • Profil anzeigen
    • LF-Net.org
Gespeichert
« Antwort #25 am: 17. February 2012, 22:08 »
Hi,

sorry dass ich mich etwas OT einmische, aber das muss jetzt einfach sein ;)
@DerHartmut und sowas von jemanden der Perl programmiert???

Meine Meinung zu C++ hab ich ja bereits geschrieben.

Grüße,
LittleFox

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #26 am: 18. February 2012, 00:03 »
Eigentlich sollte euch doch allen klar sein C++ kann gar nicht viel besser sein als C:
c == c++
Nur D ist wirklich besser. :-D (Ok. Bei anständigen Compilern hängt es noch.)
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 18. February 2012, 00:05 »
Oh, der Ansatz gefällt mir: Je nach Auswertungsreihenfolge ist allenfalls C größer als C++. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #28 am: 19. February 2012, 14:52 »
Okay, also wirklich entscheiden kann ich mich jetzt nicht ^^

Persönlich tendiere ich nach wie vor eher zu C++ weil ich ganz einfach die Sprache "schöner" und nachvollziehbarer finde als C (ja, auch wenn man mit C dasselbe anstellen kann wie mit C++)

Eigentlich sollte euch doch allen klar sein C++ kann gar nicht viel besser sein als C:
c == c++

Das hängt u.A vom Compiler ab ob C==C++  :-D

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #29 am: 23. February 2012, 20:32 »
Zwei Anmerkungen:
1. Was überhaupt nicht zu Sprache kam waren Templates für nettere Container ohne void* bzw. einmal bei der Implementierung castet man halt und nicht ständig. Ich finde, dass das Datenstrukturen allgemein um einiges angenehmer macht
2. Zur oben erwähnten RAII: Zwei weitere sehr relevante Beispiele ist std::shared_ptr für automagische Freigabe von Objekten (in meinen letzten Projekten hatte ich kein einziges delete mehr) und automatisches freigeben von Locks (das hatte ich auch mal so implementiert und fand es sehr hilfreich:
Mutex mymutex;
[...]
void f([...])
{
  // code außerhalb der critical section

  {
    scoped_lock l(mymutex); // <- ruft mymutex.lock() auf

    // code innerhalb des critical section
  } // <- exakt hier kommt der Destruktor von l und ruft mymutex.unlock() auf

  // code außerhalb des critical section
}
Das schöne daran ist, dass es schlichtweg egal ist wie man ans Ende des Blocks kommt, es wird immer der Lock freigegeben, ohne häßliches goto + rumgehacke um den return value irgendwie dann am Ende hinzukriegen.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 19. March 2012, 00:51 »
Okay, Templates sind sicherlich eine angenehme Sache. Aber jetzt auf die schnelle wüsste ich nichts
wo ich die tief im Kernel brauchen könnte. Scoped-Lock ist ein nettes Design-Pattern! Aber theoretisch liese sich das
einigermaßen simpel auch mit C realisieren.

Ich habe ein recht interessantes PDF im Netz gefunden.
Auf Seite 18 bzw. 19 kann man recht schnell sehen wie sich so etwas machen liese.
Das dürfe ganz im Sinne von taljeth sein, oder? ^^

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 19. March 2012, 13:09 »
Ich bin mir nicht so sicher, ob die Ansätze in diesem Dokument so toll sind, z.B. das new(const void* type, ...). Warum versucht man denn, verbissen irgendwas anderes schlecht zu imitieren anstatt C-Code zu schreiben? Was ist an new(Point, x, y) besser als an einem typsicheren new_point(x, y)?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 19. March 2012, 13:21 »
Naja einmal korrekt implementiert müsste man dann nie mehr ein neues "new_xyz()"
schreiben oder nicht? :)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #33 am: 19. March 2012, 14:29 »
Ja, schon. Die Frage ist nur, inwieweit man bei void* und ... von korrekt sprechen kann. ;)

Typsicherheit ist zumindest für mich dann doch auch ein Kriterium.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 19. March 2012, 14:51 »
Aber ist man auf einer gewissen Ebene (nämlch auf einer sehr niedrigen) nicht irgendwo mal am Limit?
Es stellt sich die Frage, ob man an dieser Stelle wirklich auf Typensicherheit angewiesen ist.

new() soll ja jedes Objekt anlegen egal von welchen Typen. Da ist void* doch irgendwie passend, oder nicht?

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #35 am: 19. March 2012, 16:37 »
Das kann man doch mit C11 bestimmt ganz toll machen, so ungefähr:
#define new(X, ...) *X = _Generic((*X), point: new_point)(__VA_ARGS__)

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #36 am: 19. March 2012, 19:08 »
Okay, Templates sind sicherlich eine angenehme Sache. Aber jetzt auf die schnelle wüsste ich nichts wo ich die tief im Kernel brauchen könnte.
Wie gesagt: Datenstrukturen. Zumindest ich würde meinen Monitor gegen die Wand schmeißen wenn ich die jedes Mal wieder neu implementieren müsste oder auf die Krücke void* zurückfallen würde.

Zitat
Scoped-Lock ist ein nettes Design-Pattern! Aber theoretisch liese sich das einigermaßen simpel auch mit C realisieren.
Ich wüsste nicht wie man das in C nachmachen würde und schon gar nicht "simpel".
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #37 am: 19. March 2012, 22:24 »
Wie gesagt: Datenstrukturen. Zumindest ich würde meinen Monitor gegen die Wand schmeißen wenn ich die jedes Mal wieder neu implementieren müsste oder auf die Krücke void* zurückfallen würde.
Wenn man keine Container braucht, sondern eingebettete Strukturen benutzen kann (und ich würde behaupten, das ist fast immer der Fall), dann kommt man in C mit ein bisschen Präprozessoreinsatz recht weit.

Trotzdem sind Templates natürlich schon das C++-Feature, das man in C am ehesten vermisst.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #38 am: 20. March 2012, 19:04 »
Wie gesagt: Datenstrukturen. Zumindest ich würde meinen Monitor gegen die Wand schmeißen wenn ich die jedes Mal wieder neu implementieren müsste oder auf die Krücke void* zurückfallen würde.
Wenn man keine Container braucht, sondern eingebettete Strukturen benutzen kann (und ich würde behaupten, das ist fast immer der Fall), dann kommt man in C mit ein bisschen Präprozessoreinsatz recht weit.
Dann würde ich sowas wie boost's intrusive container(s) bauen.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #39 am: 20. March 2012, 19:34 »
Ungefähr sowas baut man dann letztendlich, ja.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen