Eigenen Datentyp?
-
vip@r schrieb:
ausführen lassen will gehts nicht mehr. Was ist da jetzt falsch?
Warum definierst du in init() eine neue Variable? Die überdeckt die Membervariable dann im Scope, und du änderst nur die. Vielleicht solltest du mal Compilerwarnungen lesen, der hat dich bestimmt darauf hingewiesen.
vip@r schrieb:
Ne Klasse braucht doch einen Konstruktor und einen Destruktor oder? Wie würde der dann hier aussehen? Die beiden von grad funktionierten nicht...
Jain. Generell braucht eine Klasse keinen Konstruktor bzw. Destruktor. Zudem können diese auch vom Compiler generiert werden. Diese Klasse allerdings hätte das dringend nötig, und man merkt irgendwie, dass du nicht weißt was Destruktoren/Konstruktoren sind.
Ein Konstruktor hat keinen Rückgabewert und heißt genau so wie die Klasse. Er wird immer dann ausgeführt, wenn ein Objekt der Klasse erstellt wird. Er ersetzt hier deine init() Methode.
Ein Destruktor wird am Ende des Scopes, oder bei delete, also dann wenn das Objekt ungültig/gelöscht wird ausgeführt. Hier sollte er den im Konstruktor reservierten Speicher wieder löschen.
PS: In nicht Übungscode bitte std::vector für so etwas nutzen.
PPS: Und dieses erst = 0 setzen und dann = new irgendwas ist schlechter Stil.
PPPS: http://www.cprogramming.com/tutorial/initialization-lists-c++.html
-
cooky451 schrieb:
Ein Konstruktor hat keinen Rückgabewert und heißt genau so wie die Klasse.
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>();
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
Aberlong 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?