Eigenen Datentyp?



  • Hacker schrieb:

    Nein. Ein Konstruktor gibt automatisch eine RValue-Referenz auf this zurück. 😉
    Oder wie stellst du dir das vor:

    std::vector<int> meiner = std::vector<int>();
    

    Sowohl mit Rückgabewert, als auch mit RValue Referenz liegst du falsch. Der Konstruktor selbst gibt gar nichts zurück. Der Konstruktoraufruf T() eines Typs T erzeugt einen RValue, keine RValue Referenz.



  • 314159265358979 schrieb:

    Hacker schrieb:

    Nein. Ein Konstruktor gibt automatisch eine RValue-Referenz auf this zurück. 😉
    Oder wie stellst du dir das vor:

    std::vector<int> meiner = std::vector<int>();
    

    Sowohl mit Rückgabewert, als auch mit RValue Referenz liegst du falsch. Der Konstruktor selbst gibt gar nichts zurück. Der Konstruktoraufruf T() eines Typs T erzeugt einen RValue, keine RValue Referenz.

    Schade, einen Versuch wars wert. 🙄


  • Mod

    Ich nehme mal an, du machst das nur zur Übung und nicht, weil du std::vector nicht kennst:

    class meinDatentyp
    {
    public:
      meinDatentyp();    // Aber wo definierst du es?
      ~meinDatentyp();   // Aber wo definierst du es?
                         // Du hast richtigerweise erkannt (oder abgeschrieben ohne zu verstehen)
                         // dass du einen Destruktor brauchst. Dann brauchst du aber höchstwahscheinlich
                         // auch Zuweisungsoperator und Kopierkonstruktor (Regeld der großen Drei)
                         // Dieser Fall liegt hier vor
    
      void init(int n)    // Wenn man eine Methode schon init nennt, dann möchte man wohl einen
                          // Konstruktor schreiben
      { 
        int *feld = 0;    // Wozu?
        feld = new int [n];
    
        for(int i=0; i<n; i++)  // Du kannst auch gleich durch new initialisieren lassen
          {
            feld[i] = 0;
          }
      }                   // Semikolon brauchst du da nicht
    
      void print(int *feld, int n)  // Wenn mein eine Methode schon print nennt, dann möchte man wohl
                                    // einen Ausgabeoperator schreiben
      {
        for(int i=0; i<n; i++)      // Wäre praktisch, wenn das Feld sich seine Größe merken würde, oder?
          {
            cout << feld[i];
          }
        cout << endl;
      }                    // Semikolon brauchst du da nicht
    };                     // Hier ist es richtig
    

    Besser:

    #include<iostream>
    #include<algorithm>
    
    class meinDatentyp
    {
    private:
      int *feld;   // Feld sollte Member sein
      unsigned N;  // Die Länge brauchen wir bestimmt später noch einmal
    
    public:
      explicit meinDatentyp(unsigned N)  // Initialisierungen im Konstruktor. Dieser ist
                                         // explicit, um unerwartete Umwandlungen wie 
                                         // meinDatentyp foo = 5;  zu verhindern.
        : feld(new int[N]())    // Das () fuhrt zu Nullinitialisierung
        , N(N)
      { }                       // Die Initialisierungen in die Initialisierungslioste,
                                // der Konstruktor selbst hat nichts zu tun.
    
      ~meinDatentyp() { delete[] feld; } // Schlag mal das Stichwort "RAII" nach.
    
      meinDatentyp(const meinDatentyp& other) // Kopierkonstruktor. Wichtig, da sonst das feld
                                              // flach kopiert würde
        : feld(new int[other.N])
        , N(other.N)
      { // Dieses Mal muss der Konstruktor tatsächlich arbeiten. Machen wir eine Kopie der Daten:
        std::copy(other.feld, other.feld + N, feld);
      }
    
      // Den Zuweisungsoperator implementieren wir mittels Copy&Swap Idiom. Das verhindert
      // Probleme, falls new eine Exception wirft (Speicher voll)
      meinDatentyp& operator=(const meinDatentyp &other)  
      {
        if (this != &other) // Selbstzuweisung verhindern
          {
            meinDatentyp temp(other); // Kopie mittels Kopierkonstruktor erzeugen
            // Wenn alles gut ging, tauschen:
            std::swap(feld, temp.feld);
            std::swap(N, temp.N);
          } // Und hier werden die alten Ressourcen von temps Destruktor freigegeben
        return *this;
      }
    
      // Für die Ausgabe überladen wir den Operator << für ostream und meinDatentyp.
      // Da wir den Code von ostream nicht haben, muss es eine globale Funktion sein.
      // Damit sie trotzdem das feld sieht, machen wir sie einen friend. Und dann können
      // wir sie auch gleich in der Klasse definieren, der Compiler findet dir Funktion dann
      // dank argument dependend lookup
      friend std::ostream& operator<<(std::ostream &out, const meinDatentyp &data)
      {
        for (unsigned i = 0; i < data.N; ++i)
          out << data.feld[i] << ' ';
        return out;
      }
    };
    
    // Kurzer Test:
    int main()
    {
      meinDatentyp foo(5);
      std::cout << "Foo: " << foo << '\n'; // Gibt 5 Nullen aus.
    
      meinDatentyp bar = foo;
      std::cout << "Bar: " << bar << '\n'; // Gibt 5 Nullen aus.
    
      bar = bar; // Trotz solcher Sauereien keine Speicherlöcher.
    }
    


  • SeppJ schrieb:

    Das () fuhrt zu Nullinitialisierung

    Das kannte ich noch nicht. Stand auch in keinem Buch/Tutorial/FAQ was ich bis jetzt gelesen habe.


  • Mod

    pyhax schrieb:

    SeppJ schrieb:

    Das () fuhrt zu Nullinitialisierung

    Das kannte ich noch nicht. Stand auch in keinem Buch/Tutorial/FAQ was ich bis jetzt gelesen habe.

    Ja, das ist ein bekannter Fakt, dass diese Art der Initialisierung nur wenige Leute kennen. Deswegen habe ich sie auch absichtlich benutzt. Dies nutzt die Klausel, dass der Wert bei dieser Form der Wert default initialized wird, was für ein Arrays bedeutet, dass die Elemente value initialized werden, was für ints bedeutet, dass sie zero initialized werden 🕶 .

    In C++11 kann man mWn an der Stelle auch eine initializer list angeben (müsste ich aber nachschlagen), in C++98 kann man bloß diese Nullinitialisierung durchführen und müsste andere Werte von Hand (oder indirekt mit fill oder copy) erzeugen. Aber da der Threadersteller ausdrücklich Nullinitialisierung wollte (vermutlich ohne tieferen Grund, aber egal), war das eine passende Gelegenheit, das mal zu nutzen.



  • SeppJ schrieb:

    pyhax schrieb:

    SeppJ schrieb:

    Das () fuhrt zu Nullinitialisierung

    Das kannte ich noch nicht. Stand auch in keinem Buch/Tutorial/FAQ was ich bis jetzt gelesen habe.

    Ja, das ist ein bekannter Fakt, dass diese Art der Initialisierung nur wenige Leute kennen.

    Es ist auch ein bekannter Fakt, dass vector nur wenige Leute kennen.


  • Mod

    TyRoXx schrieb:

    Es ist auch ein bekannter Fakt, dass vector nur wenige Leute kennen.

    Der Threadersteller vielleicht nicht. Aber new mit () kennen selbst die Forenmitglieder mit hunderten von Beiträgen nicht unbedingt oder wenn sie es kennen, dann wissen sie nicht, was das in diesem Fall genau bedeutet. Ich habe selber kurz nachgeschlagen, ob die Syntax auch wirklich stimmt (wann benutzt man sonst schon new[]?). Ich wusste nur, dass es existiert und ungefähr so aussieht.



  • new int[N]()

    Steckt da noch mehr dahinter?
    Kann das syntaktisch noch anders aussehen?
    Wie heißt das Konstrukt?

    Mir wars auch völlig unbekannt.


  • Mod

    Alle Zitate aus C++98, in C++11 sind die Formulierungen etwas anders, aber laufen auf das gleiche hinaus. Hervorhebung durch mich:

    5.3.4/1:

    new-expression:
    ::opt new new-placementopt new-type-id new-initializeropt

    ...

    new-initializer:
    ( expression-listopt] )

    Man beschte, die expression-list ist optional!

    5.3.4/15:

    A new-expression that creates an object of type T initializes that object as follows:
    ...
    — If the new-initializer is of the form (), default-initialization shall be performed (8.5);

    8.5/5:

    To default-initialize an object of type T means:
    — if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is
    ill-formed if T has no accessible default constructor);
    — if T is an array type, each element is default-initialized;
    — otherwise, the storage for the object is zero-initialized.

    8.5/5:

    To zero-initialize storage for an object of type T means:
    — if T is a scalar type (3.9), the storage is set to the value of 0 (zero) converted to T;
    — if T is a non-union class type, the storage for each nonstatic data member and each base-class subobject
    is zero-initialized;
    — if T is a union type, the storage for its first data member89) is zero-initialized;
    — if T is an array type, the storage for each element is zero-initialized;
    — if T is a reference type, no initialization is performed.

    3.9/10:

    Arithmetic types (3.9.1), enumeration types, pointer types, and pointer to member types (3.9.2), and cv-
    qualified versions of these types (3.9.3) are collectively called scalar types.

    3.9.1/10:

    Integral and floating types are collectively
    called arithmetic types.

    3.9.1/7:

    Types bool, char, wchar_t, and the signed and unsigned integer types are collectively called integral types.

    3.9.1/2:

    There are four signed integer types: “signed char”, “short int”, “int”, and “long int.” In this
    list, each type provides at least as much storage as those preceding it in the list.

    Puh! Und damit folgt mit meiner obigen Erklärung, dass new int[N]() ein Array mit N ints erzeugt und die auch wirklich garantiert alle 0 sind. Was passiert, wenn man da was anderes hinschreibt, möge man sich selber erarbeiten. Die relevanten Stellen habe ich ja angegeben.



  • Interessant.
    Danke für die Mühe 👍



  • SeppJ schrieb:

    3.9.1/2:

    There are four signed integer types: “signed char”, “short int”, “int”, and “long int.” In this
    list, each type provides at least as much storage as those preceding it in the list.

    `

    long long int` ist sein C++11 standardisiert.


  • Mod

    Hacker schrieb:

    `

    long long int` ist sein C++11 standardisiert.

    Schon komisch, dass der

    SeppJ schrieb:

    C++98

    Standard das nicht weiß 🙄



  • SeppJ schrieb:

    Hacker schrieb:

    `

    long long int` ist sein C++11 standardisiert.

    Schon komisch, dass der

    SeppJ schrieb:

    C++98

    Standard das nicht weiß 🙄

    Ich les mal wieder zu wenig 😃
    Aber long long gab es doch schon seit C++98?



  • Nein. Viele (so gut wie alle) Compiler haben es als Erweiterung angeboten, aber Standard war es vor C++11 nicht.



  • SeppJ schrieb:

    Hacker schrieb:

    `

    long long int` ist sein C++11 standardisiert.

    Schon komisch, dass der

    SeppJ schrieb:

    C++98

    Standard das nicht weiß 🙄

    ich wette, da gibt es noch mehr Dinge im C++11 Standard, die der 98er Standard noch nicht kennt 😉



  • daddy_felix schrieb:

    SeppJ schrieb:

    Hacker schrieb:

    `

    long long int` ist sein C++11 standardisiert.

    Schon komisch, dass der

    SeppJ schrieb:

    C++98

    Standard das nicht weiß 🙄

    ich wette, da gibt es noch mehr Dinge im C++11 Standard, die der 98er Standard noch nicht kennt 😉

    Ich wette dagegen. Denn der C++11 Standard beinhaltet so ungefähr alles, was der C++98 auch beinhaltet (bis auf die Sachen die syntaktisch verändert wurden). int beinhalten auch beide.

    Und das "noch" kannst du wegmachen, wieso noch?



  • Der C++98 Standard hatte 715 Seiten, C++11 hat 1310. Nur mal so als Einwurf.



  • 314159265358979 schrieb:

    Der C++98 Standard hatte 715 Seiten, C++11 hat 1310. Nur mal so als Einwurf.

    Das gibts ja garnich' 😮
    So viel neues?



  • 314159265358979 schrieb:

    Der C++98 Standard hatte 715 Seiten, C++11 hat 1310. Nur mal so als Einwurf.

    Hab die das http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#334 angehangen?



  • Hacker schrieb:

    daddy_felix schrieb:

    ich wette, da gibt es noch mehr Dinge im C++11 Standard, die der 98er Standard noch nicht kennt 😉

    Ich wette dagegen. Denn der C++11 Standard beinhaltet so ungefähr alles, was der C++98 auch beinhaltet (bis auf die Sachen die syntaktisch verändert wurden). int beinhalten auch beide.

    Und das "noch" kannst du wegmachen, wieso noch?

    ➡

    Hacker schrieb:

    Ich les mal wieder zu wenig 😃


Anmelden zum Antworten