Speicherverwaltung



  • Vielen Dank für die schnelle Hilfe.
    Das mit dem Named-Constructor Idiom ist eine coole Sache - wobei der Destructor ja durchaus Public sein dürfte.



  • matti83 schrieb:

    [...]wobei der Destructor ja durchaus Public sein dürfte.

    Genau Dann reicht auch ein einfaches delete auf den Zeiger. Was der ganze Quatsch mit den privaten Dtors und die daraus resultierenden Gruselkonstrukte sollen, erschließt sich mir absolut nicht.



  • plaissment schrieb:

    Absolut hässlich, nicht threadsafe und daher nicht zu empfehlen:

    #include <iostream>
    
    class nostack {
      nostack(const nostack& other);
      static nostack *to_construct;
    public:
      nostack() {
        if (this != to_construct) throw std::bad_alloc();
        else to_construct = 0;
      }
      void *operator new(std::size_t num_bytes) {
        return reinterpret_cast<void *>
          (to_construct = reinterpret_cast<nostack *>(new char[num_bytes]));
      }
      void operator delete(void *memory, std::size_t) {
        return delete[] reinterpret_cast<char *>(memory);
      }
    };
    nostack * nostack::to_construct;
    
    int main()
    {
      std::cout << "stack: ";
      try { { nostack a; } std::cout << "geglückt\n"; }
      catch(std::bad_alloc) { std::cout << "missglückt\n"; }
    
      std::cout << "heap : ";
      try { { nostack *b = new nostack; delete b; } std::cout << "geglückt\n"; }
      catch(std::bad_alloc) { std::cout << "missglückt\n"; }
    }
    
    stack: missglückt
    heap : geglückt
    

    Das mit dem create_xx ist da schon viel besser.

    Grundsätzlich sehe ich aber keinen Grund, wieso man verbieten sollte, etwas auf dem Stack anzulegen.

    Kann ich so denn wirklich ausschliessen, dass es sich auf dem stack oder dem heap befindet? oder kann ich so nur sagen, welcher Constructor benutzt wurde.

    Denn was passiert denn, wenn ich diese Klasse als Memberobjekt einer anderen Klasse verwende und diese mit new erstelle?

    class Damn{
     public:
      nostack ns;
    };
    
    Damn* d = new Damn();
    


  • matti83 schrieb:

    Gibt es eine Möglichkeit zu verbieten oder zu erkennen, dass ein Objekt dieser Klasse mit der ersten Variante angelegt wurde?

    Nein. Und die Codes in diesem Thread sind (abgesehen von Tachyons) grauenhaft und führen zu undefiniertem Verhalten.

    Warum es nicht sinnvoll ist, dass eine Klasse versucht, Freestore/Stack zu unterscheiden, erfahrt ihr hier. Die wesentliche Punkte sind: Das Vorgehen ist alles andere als benutzerfreundlich, sondern wehrt sich gegen die Intuition des Anwenders und gängige C++-Praxis. Es nimmt dem Anwender die Möglichkeit, den Speicherverwaltungsmechanismus zu bestimmen und führt zu unerwarteten Fehlern.

    Lest unbedingt Shade Of Mines Beiträge im verlinkten Thread, er bringt es sehr gut auf den Punkt.



  • Hmm.. hab es mir durchgelesen. Fazit: es geht nicht!? 😕



  • Nein. Fazit: Es macht keinen Sinn, das zu versuchen.

    Es geht zwar auch nicht, das stimmt, aber selbst wenn es ginge, wäre das Vorgehen nicht zu empfehlen.



  • Will ich irgendwie nicht verstehen! Was Sinn macht und was nicht bleibt doch dem Künstler überlassen.



  • matti83 schrieb:

    Will ich irgendwie nicht verstehen! Was Sinn macht und was nicht bleibt doch dem Künstler überlassen.

    Nein, du kannst nicht nur an dich gerade jetzt denken. Zumindest falls du später mal vorhast, dass andere Leute deinen Code lesen oder sogar benutzen sollen (in Form einer Bibliothek oder einem gemeinsamen Projekt). Oder auch falls du selbst in zwei Jahren nochmals diesen Code anschaust und dann über deine eigene "Benutzerfreundlichkeit" stolperst.

    Und "Künstler"... Naja, Programmieren beinhaltet zwar viele kreative Aspekte, aber auch sehr viele festgelegte Regeln. Und du musst dich an die Konzepte der Sprache halten, in der du programmierst. In C++ wäre so ein Konzept: Programmiere nicht wie in Java. D.h. bevormunde den Benutzer nicht. Wenn der Benutzer seine Objekte dynamisch auf dem Heap anlegt, masse dir nicht an, diese zu löschen (es sei denn, du hast eine konsequente eindeutige Semantik und kein intransparentes Gemisch mit Stack-Objekten).

    Aber hast du den anderen Thread wirklich gelesen? Ich begreife nämlich nicht ganz, wie man nach all den Argumenten immer noch eine Heap/Stack-Unterscheidung durchführen will. Denn grundsätzlich bist du sicher daran interessiert, mehr oder weniger "sauberen" Code zu haben. Das bezieht sich auf Punkte wie Wartbarkeit, Übersicht, Aussagekraft, Fehleranfälligkeit, Einfachheit etc. Es hat nur Vorteile, wenn du deinen Code intuitiv schreibst. Das kannst du sicher nachvollziehen, oder?



  • ich bin dabei mir ein GC zu schreiben. Jetzt habe ich einer Pointerklasse, die auf meine Objekte zeigt. Diese Pointerklasse kann jetzt auf dem Stack liegen oder im Heap. Aber nur wenn sie im Stack ist interessiert sie mich. Die andere will ich ignorieren. Im heap ist sie, wenn sie z.B. selbst member eines Objektes ist.



  • Tachyon schrieb:

    Mach die Konstruktoren private, und biete statische Klassenfunktionen an, mit welchen Du die privaten Konstruktoren aufrufst:

    class C
    {
    public:
       static std::shared_ptr<C> create(args)
       {
          return std::shared_ptr<C>(new C(args));
       }
    private:
       C(args){...}
    };
    

    Siehe hierzu auch nach dem Named-Constructor Idiom.

    Immer den teuersten aller Pointer empfehlen...
    shared_ptr wird viel zu häufig missbraucht, weil sich Entwickler zu wenig
    Gedanken über Objektverantwortungen machen...



  • matti83^^ schrieb:

    ich bin dabei mir ein GC zu schreiben. Jetzt habe ich einer Pointerklasse, die auf meine Objekte zeigt. Diese Pointerklasse kann jetzt auf dem Stack liegen oder im Heap. Aber nur wenn sie im Stack ist interessiert sie mich. Die andere will ich ignorieren. Im heap ist sie, wenn sie z.B. selbst member eines Objektes ist.

    Falls du der Gleiche bist wie matti83: Du hast rein gar nicht verstanden, was ich und die Leute im verlinkten Thread sagen wollten. Lies dir den Thread richtig durch und überleg dir in Ruhe, warum dein Vorhaben Unsinn ist.

    Yeah... I like it... schrieb:

    Immer den teuersten aller Pointer empfehlen...
    shared_ptr wird viel zu häufig missbraucht, weil sich Entwickler zu wenig
    Gedanken über Objektverantwortungen machen...

    Gut, dass ich nicht der einzige bin, der das so sieht. 👍



  • Hallo Nexus.

    Anscheinend hab ich es wirklich nicht verstanden. Ich wäre dir sehr verbunden, wenn du mir kurz und knapp den Kern der Aussage hier einmal versuchst wieder zu spiegeln.

    Denn unter Umständen hab ich es auch nicht geschafft dir mein Problem vernünftig zu schildern.

    Freundliche Grüße



  • Vielleicht haben wir auch aneinander vorbeigeredet. Nochmals die wichtigsten Gründe, warum es nicht sinnvoll ist, wenn eine Klasse sich unterschiedlich verhält, wenn ihre Objekte automatisch oder dynamisch angefordert wurden. Ganz unabhängig davon, ob die Unterscheidung überhaupt realisierbar ist.

    • Du versuchst mit dem Ansatz, benutzerfreundlich zu sein, und den Speicher automatisch freizugeben, wenn er vom Benutzer dynamisch angefordert wurde. Die Idee ist prinzipiell sehr gut, aber das mit der Benutzerfreundlichkeit geht komplett nach hinten los.
    • Der Grund ist, dass du Konzepte aus anderen Sprachen (Garbage Collector) 1:1 nach C++ übertragen willst, wo Speicherverwaltung aber ganz anders abläuft. C++ hat viel strengere und eindeutigere Besitzverhältnisse als Sprachen mit GC (Speicher ist nicht geteilt, sondern hat meist einen klaren Besitzer). Normalerweise wird der Speicher von dem freigegeben, der ihn anfordert. Mit deinem Versuch verstösst du gegen diese Konvention.
    • Dadurch rufst du beim Benutzer Verwirrung hervor. Wenn der Benutzer ein einfaches new verwendet, geht er davon aus, den Speicher ohne Probleme mit delete freigeben zu können. Er wird sich wundern, wenn ganz normaler Code einfach abstürzt.
    • Gleichzeitig nimmst du dem Anwender mit dem GC die Freiheit, sich Speicher irgendwie zu verschaffen. Er kann den Stack benutzen, den Heap ( malloc() aus C), den Freestore ( new ) oder irgendeinen anderen Allokator. Mit einem automatischen delete in deiner Bibliothek zwingst du ihn zur Verwendung des new -Operators.
    • In C++ benutzt man das RAII-Idiom, um manuelle Speicherverwaltung zu vermeiden. Damit muss gar niemand mehr explizit den Speicher freigeben, sondern die Aufgabe wird an eine Klasse (z.B. Smart-Pointer) delegiert. Damit ist der Grund dafür, überhaupt einen automatischen Freigabemechanismus in deiner Bibliothek einzurichten, hinfällig.


  • * Du versuchst mit dem Ansatz, benutzerfreundlich zu sein, und den Speicher automatisch freizugeben, wenn er vom Benutzer dynamisch angefordert wurde. Die Idee ist prinzipiell sehr gut, aber das mit der Benutzerfreundlichkeit geht komplett nach hinten los.

    Den Punkt kann ich abhaken, weil der Benutzer genau weiss was er macht, wenn er meinen GC nutzt

    * Der Grund ist, dass du Konzepte aus anderen Sprachen (Garbage Collector) 1:1 nach C++ übertragen willst, wo Speicherverwaltung aber ganz anders abläuft. C++ hat viel strengere und eindeutigere Besitzverhältnisse als Sprachen mit GC (Speicher ist nicht geteilt, sondern hat meist einen klaren Besitzer). Normalerweise wird der Speicher von dem freigegeben, der ihn anfordert. Mit deinem Versuch verstösst du gegen diese Konvention.

    Den Speicher den ich freigebe, kann man auch nur aus meinem GC anfordern. Also hat den auch kein anderer freizugeben. Das mit dem Sprachübergreifend lassen wir mal. In C++ kann man auch nur Speicher anlegen und wieder freigeben. Und da der Benutzer keinen Speicher anfordet, sondern mein GC ist alles gut.

    * Dadurch rufst du beim Benutzer Verwirrung hervor. Wenn der Benutzer ein einfaches new verwendet, geht er davon aus, den Speicher ohne Probleme mit delete freigeben zu können. Er wird sich wundern, wenn ganz normaler Code einfach abstürzt.

    Beziehe mich auf Punkt 2 - auf die Objekte kann er kein new machen.

    * Gleichzeitig nimmst du dem Anwender mit dem GC die Freiheit, sich Speicher irgendwie zu verschaffen. Er kann den Stack benutzen, den Heap (malloc() aus C), den Freestore (new) oder irgendeinen anderen Allokator. Mit einem automatischen delete in deiner Bibliothek zwingst du ihn zur Verwendung des new-Operators.

    Diese Freiheit behält der Benutzer für jegliche Klassen, die er selbst baut und die nicht zu meinem GC gehören.

    * In C++ benutzt man das RAII-Idiom, um manuelle Speicherverwaltung zu vermeiden. Damit muss gar niemand mehr explizit den Speicher freigeben, sondern die Aufgabe wird an eine Klasse (z.B. Smart-Pointer) delegiert. Damit ist der Grund dafür, überhaupt einen automatischen Freigabemechanismus in deiner Bibliothek einzurichten, hinfällig.

    Auch ich benutze RAII - dennoch wüsste ich gern ob das Objekt auf dem Stack oder auf dem Heap ist.

    Ich benötige diese Info nicht um das Objekt zu löschen, sondern um gewisse Auswertungen zu machen.

    Also meine alte Frage ob ich diese Unterscheidung zwischen Stack und Heap heraus bekommen kann. Ich wüsste auch nicht warum das nicht gehen sollte.



  • matti83 schrieb:

    * Du versuchst mit dem Ansatz, benutzerfreundlich zu sein, und den Speicher automatisch freizugeben, wenn er vom Benutzer dynamisch angefordert wurde. Die Idee ist prinzipiell sehr gut, aber das mit der Benutzerfreundlichkeit geht komplett nach hinten los.

    Den Punkt kann ich abhaken, weil der Benutzer genau weiss was er macht, wenn er meinen GC nutzt

    Dann kann ich nur hoffen, das der Benutzer ausschließlich mit deinen GC arbeiten muss, damit er nicht ständig durcheinander geraten kann. Ein GC widerspricht der C++ Programmierung weitgehend (Thema RAII).

    Wobei durchaus (für ein speziellen TR ausgelagert) eine Überlegung gibt einen optionalen C++ GC einzuführen, den man aber entweder nur ganz oder garnicht aktivieren kann (War ursprünglich für den kommenden Standard in Diskussion).

    matti83 schrieb:

    Und da der Benutzer keinen Speicher anfordet, sondern mein GC ist alles gut.

    Aber nur wenn sichergestellt ist, das der Speicher AUSSCHLIESSLICH von dir verwaltet wird. Was man wohl ausschließen muss, wenn man nicht jegliche externe Bibliothek ausschließen will.



  • matti83 schrieb:

    * Der Grund ist, dass du Konzepte aus anderen Sprachen (Garbage Collector) 1:1 nach C++ übertragen willst, wo Speicherverwaltung aber ganz anders abläuft. C++ hat viel strengere und eindeutigere Besitzverhältnisse als Sprachen mit GC (Speicher ist nicht geteilt, sondern hat meist einen klaren Besitzer). Normalerweise wird der Speicher von dem freigegeben, der ihn anfordert. Mit deinem Versuch verstösst du gegen diese Konvention.

    Den Speicher den ich freigebe, kann man auch nur aus meinem GC anfordern. Also hat den auch kein anderer freizugeben. Das mit dem Sprachübergreifend lassen wir mal. In C++ kann man auch nur Speicher anlegen und wieder freigeben. Und da der Benutzer keinen Speicher anfordet, sondern mein GC ist alles gut.

    Wenn du nur selber den Speicher anforderst ist es ja easy. Dann weisst du ja welchen Speicher du angefordert hast und weisst somit ob du ihn löschen kannst oder nicht.

    Insofern ist es ja keine Frage von stack vs heap - weil du ja eh selber allokierst. Einfach merken was du gemacht hast (das musst du ja sowieso wenn der GC etwas taugen soll). Damit kannst du ganz einfach selber testen ob die allokierung dynamisch stattgefunden hat oder nicht.

    Prinzipiell ist so ein GC natürlich blödsinn, aber davon werden wir dich nicht abbringen können fürchte ich.



  • matti83 schrieb:

    Also meine alte Frage ob ich diese Unterscheidung zwischen Stack und Heap heraus bekommen kann. Ich wüsste auch nicht warum das nicht gehen sollte.

    Was für Auswertungen sollen das sein?
    Du könntest den new-Operator überschreiben und dann daran erkennen, wenn ein Objekt mit new angelegt wird...



  • Powerpaule schrieb:

    matti83 schrieb:

    Also meine alte Frage ob ich diese Unterscheidung zwischen Stack und Heap heraus bekommen kann. Ich wüsste auch nicht warum das nicht gehen sollte.

    Was für Auswertungen sollen das sein?
    Du könntest den new-Operator überschreiben und dann daran erkennen, wenn ein Objekt mit new angelegt wird...

    Genau das hab ich jetzt auch vor. Problem ist noch: Welcher Konstruktor oder Operator wird aufgerufen, wenn ich kein NEW benutze? Oder ist dann sichergestellt, dass der Speicher mit 0 initialisiert ist?

    Also Membervariable: char stack;
    Ohne NEW => stack = 0;
    Mit NEW => stack = 1; weil ich den new operator überschreibe und stack auf 1 setze

    oder ist bei Ohne NEW der Speicherinhalt undefiniert? 😕



  • dh also dein GC allokiert den Speicher doch nicht. Damit wird es nur sinnloser (falls das moeglich ist). Du kannst nur operator new/delete ueberladen um Speicher zu allokieren, nicht um die Objekte zu erstellen. (operator new/delete zu ueberladen bringt dir also nichts, da du damit nicht die objekte erstellst sondern nur rohen speicher beschaffst).

    Sprich du musst dir merken welche adressen du allokiert hast. Dann kannst du spaeter herausfinden ob die adresse allokiert wurde (und somit du den Speicher verwaltest) oder ob das Objekt auf eine andere Art erstellt wurde (bedenke: es muss nicht zwangslaeufig auf den stack erstellt worden sein nur weil kein new aufgerufen wurde).

    Was du willst ist keine gute Idee. Es wird nicht vernuenftig funktionieren. Lass uns lieber ueber alternativen nachdenken.

    Wenn du einen GC haben willst, dann gibt es naemlich Ansaetze wie man welche verwenden kann die funktionieren. Das Problem ist nur, dass dein Ansatz falsch ist.



  • Sorry .. also ihr interpretiert mir da viel zu sehr, als meine Frage zu beantworten 😋

    ich will nur wissen ob es aufm Stack ist oder nicht.

    char istAufStack( void* p )
    {
      char onStack;
      void* sp = (void*)&onStack;
      return ( p < sp ? 1 : 0 );
    }
    
    class Pointer
    {
      private:
        char stack;
      public:
      Pointer()
      {
        stack = ::istAufStack( (void*)this );
      }
    };
    

Anmelden zum Antworten