Dynarray Implementation


  • Mod

    TyRoXx schrieb:

    Der Destruktor ist implizit noexcept(true) .

    oh?

    Sone schrieb:

    Soll ich vom Allokator ableiten, oder wie soll ich das verstehen?

    Genau. Alternativ könnte auch eine Hilfsklasse verwendet werden, die alle Datenmember aufnimmt.



  • Ein dynarray mit nem Allocator macht doch überhaupt keinen Sinn ...?
    Weiters macht dynarray auch keinen Sinn so lange der Compiler es nicht versteht die Speicherallokation im Falle des Falles in ein alloca umzuwandeln.
    Ohne diesen Vorteil ist dynarray nämlich ein vollkommen sinnfreier Container.



  • TyRoXx schrieb:

    Das häufige Wechseln zwischen public und private macht die Klasse unübersichtlich.

    Done.

    Dazu jetzt noch mehr Kommentare für die Helfer-Funktionen.



  • camper schrieb:

    TyRoXx schrieb:

    Der Destruktor ist implizit noexcept(true) .

    oh?

    Ich glaube, TyRoXx verwechselt da den Dtor mit Deallokationsfunktionen. Den zu Dtors mit impliziter noexcept -Spezifikation habe ich nicht gefunden, aber mehrmals das hier:

    N3337 §12.5 / 9 schrieb:

    If a deallocation function has no explicit exception-specification, it is treated as if it were specified
    with noexcept(true)



  • Sone schrieb:

    TyRoXx schrieb:

    Warum verschiedene Namensschemata? Ich sehe mindestens drei: my_iterator_traits , Dynarray , _constructWith

    Für verschachtelte Klassen nehme ich die erste Benennung. Für ganz gewöhnliche Klassen(-templates) nehme ich UpperCamelCase, und für private Hilfsfunktionen nehme ich lowerCamelCase mit einem Unterstrich davor.

    unabhaengig von der Sinnhaftigkeit davon: deine Namensgebung ist nicht einheitlich.
    zB _destroy_all und _deallocateAll

    Check nochmal dein Aufraeumen wenn beim kopieren/initialisieren eine Exception fliegt. _destroy_all sieht da fehlerhaft aus.

    Nebenbei bemerkt: du verlangst dass die itertoaren auch verringert werden duerfen - was nicht unbedingt notwendig ist.

    Ich wuerde die einzelnen Methoden weiters noch per assert schuetzen. Und natuerlich um iterator debugging erweitern.



  • Hmm... begin/end ist noexcept ... rbegin/rend aber nicht.



  • Und ich muss nochmal nachfragen:

    Dynarray& operator=(Dynarray const& tmp)
        {
            Dynarray d(tmp);
            swap(d);
            return *this;
        }
    

    Warum recyclest du hier keinen Speicher? Das ist trivial umsetzbar und macht in der Praxis einen Unterschied.
    Solange der aktuelle allozierte Speicher ausreicht gibt es keinen Grund ihn freizugeben und neuen zu allozieren.



  • Ethon schrieb:

    Solange der aktuelle allozierte Speicher ausreicht gibt es keinen Grund ihn freizugeben und neuen zu allozieren.

    Doch. Man kann dann keine starke Exception-Garantie mehr geben.


  • Mod

    Sone schrieb:

    camper schrieb:

    TyRoXx schrieb:

    Der Destruktor ist implizit noexcept(true) .

    oh?

    Ich glaube, TyRoXx verwechselt da den Dtor mit Deallokationsfunktionen.

    Nicht unbedingt.

    n3337 schrieb:

    12.4 Destructors [class.dtor]
    [...]
    3 A declaration of a destructor that does not have an exception-specification is implicitly considered to have
    the same exception-specification as an implicit declaration (15.4).

    Die Exceptionspezifikation eines implizit deklarierten Destruktors hängt von den Exceptionsspezifikationen der Funktionen ab, die von diesem aufgerufen würden, d.h. den Destruktoren der Member und der direkten oder virtuellen Basisklassen. Wenn keiner dieser Destruktoren direkt oder indirekt eine explizite Exceptionspezifikation erhält, läuft das somit tatsächlich im Allgemeinen auf eine Sepzifikation noexcept(true) hinaus.
    Die Exceptionspezifikation ist mAn Teil der Schnittstelle und sollte somit nicht implizit von privaten Details der Klasse abhängen - deshalb halte ich eine explizite Angabe für sinnvoll. Der Standard macht es anders, man kann also durchaus auch andere Ansicht sein.



  • Lol, 5 Seiten Ratschläge und es funktioniert immer noch nicht.
    Abgesehen von den Warnungen, wenn man deinen Code kompiliert (schonmal mit -Wall -Wextra getestet?), es hat noch einige Bugs.

    Hier mal ein einfaches Testprogramm:

    int instances = 0;
    
    void may_throw()
    {
      if (rand()%100 == 0)
        throw 0;
    }
    
    struct test {
      test() { ++instances; may_throw(); }
      test(test const&) { ++instances; may_throw(); }
     ~test() { --instances; }
    };
    
    int main()
    {
      srand(0);
    
      try { Dynarray<test> d(1000); }
      catch (...) {}
    
      std::cout << instances << '\n';
    }
    

    Wenn Zeile 12 etwas abändert, dürfte der Fehler etwas klarer werden.

    ~test() { std::cout << --instances << '\n'; }
    


  • iksepschensaif schrieb:

    Lol, 5 Seiten Ratschläge und es funktioniert immer noch nicht.
    Abgesehen von den Warnungen, wenn man deinen Code kompiliert (schonmal mit -Wall -Wextra getestet?), es hat noch einige Bugs.

    Hier mal ein einfaches Testprogramm:

    int instances = 0;
    
    void may_throw()
    {
      if (rand()%100 == 0)
        throw 0;
    }
    
    struct test {
      test() { ++instances; may_throw(); }
      test(test const&) { ++instances; may_throw(); }
     ~test() { --instances; }
    };
    
    int main()
    {
      srand(0);
    
      try { Dynarray<test> d(1000); }
      catch (...) {}
    
      std::cout << instances << '\n';
    }
    

    Wenn Zeile 12 etwas abändert, dürfte der Fehler etwas klarer werden.

    ~test() { std::cout << --instances << '\n'; }
    

    Gefixed. Da war ein Missverständnis, ich dachte der Dtor wird dann nicht mehr aufgerufen ... jetzt ist das geklärt. (Und noch eine kleine Fallunterscheidung für den einen delegation Ctor 🙂 )

    Vielen Dank :xmas1:

    Edit: o.O eine bleibt immer übrig. Setz mich dran...



  • Ach, dein Beispiel ist doch bescheuert.
    Man muss das may_throw vor die Inkrementierung setzen, ist doch klar.

    Edit: Ihr seit ganz sicher, dass man bei einer Exception aus dem Dtor einfach nix aufräumen muss?



  • Ich hab sicherheitshalber mal hinzugefügt, dass wenn eine Exception im Dtor des zu zerstörenden Objektes fliegt (in _destroyAllUntil ), einfach alles deallokiert wird und der Zeiger auf Null gesetzt wird (damit der Dtor übersprungen wird).



  • Sone schrieb:

    Ich hab sicherheitshalber mal hinzugefügt, dass wenn eine Exception im Dtor des zu zerstörenden Objektes fliegt (in _destroyAllUntil ), einfach alles deallokiert wird und der Zeiger auf Null gesetzt wird (damit der Dtor übersprungen wird).

    Dir fehlen Grundlagen. Lies mal das durch: http://home.ustc.edu.cn/~zixin/More%20Effective%20C++/MAGAZINE/SU_FRAME.HTM#destruct



  • iksepschensaif schrieb:

    Sone schrieb:

    Ich hab sicherheitshalber mal hinzugefügt, dass wenn eine Exception im Dtor des zu zerstörenden Objektes fliegt (in _destroyAllUntil ), einfach alles deallokiert wird und der Zeiger auf Null gesetzt wird (damit der Dtor übersprungen wird).

    Dir fehlen Grundlagen. Lies mal das durch: http://home.ustc.edu.cn/~zixin/More%20Effective%20C++/MAGAZINE/SU_FRAME.HTM#destruct

    Ja, aber wenn ich wirklich so tue als ob nichts passiert, und es gibt doch irgendeinen doofen Programmierer der einen Destruktor eine Exception emittieren lässt, dann ... leckt der ganze Speicher... was ist so falsch, einfach auf diesen Fall einzugehen?
    Wieso muss man immer so tun als ob es keine schlechten User gibt?
    Das ein Dtor nie eine Exception nach außen kommen lassen darf, ist mir schon klar.

    Edit: Btw: Geändert.



  • [...] with both of these is that they have fundamentally the same problem we just described for destroy! For example, consider the following code: ¤ Sutter, P145

    T* p = new T[10];
    delete[] p;
    

    Looks like normal harmless C++, doesn't it? But have you ever wondered what new[] and delete[] do if a T destructor throws? Even if you have wondered, you can't know the answer for the simple reason that there is none: The standard says you get undefined behavior if a T destructor throws anywhere in this code, which means that any code that allocates or deallocates an array of objects whose destructors could throw can result in undefined behavior. [...]

    Hast dus nicht gelesen? Undefinded bleibt undefined, egal ob man drauf reagiert. :xmas1:



  • ScottZhang schrieb:

    Hast dus nicht gelesen? Undefinded bleibt undefined, egal ob man drauf reagiert. :xmas1:

    Ach, es ist undefiniertes Verhalten? Na dann ^^



  • Sone schrieb:

    Wieso muss man immer so tun als ob es keine schlechten User gibt?

    Weil der Code, wenn du ihn auf schlechte User auslegst, selbst schlecht wird.

    Natürlich kannst du die üblichen Fehlerquellen durch Compiletime-Mechanismen oder Assertions abfangen, die passieren ja auch guten Usern. Aber wenn du anfängst, selbstverständliche Dinge massiv zu verkomplizieren, nur weil jemand sie mit Absicht (oder grober Dummheit) falsch verwenden könnte, ist etwas nicht mehr gut. Solchen Leuten darf das Programm ruhig mit voller Wucht um die Ohren fliegen.

    <:xmas2:>



  • glühnase schrieb:

    Sone schrieb:

    Wieso muss man immer so tun als ob es keine schlechten User gibt?

    Weil der Code, wenn du ihn auf schlechte User auslegst, selbst schlecht wird.

    👍
    Das ist mal ein Merksatz.



  • *push*
    So, wie sieht's aus? Unschönheiten usw. muss es doch bestimmt noch geben. :xmas1:

    Edit: Ich setz' mich am besten mal an einen ausführlichen Test.


Anmelden zum Antworten