Arraygröße bei Übergabe herausfinden
-
Ich meine sowas:
template <unsigned int Size> void function(int (&array)[Size]);P.S. Wenn du produktive Dinge programmieren willst, zahlt sich die STL schnell aus, da sie sicher (Compilezeit- und Laufzeitprüfungen), hochoptimiert und bugfrei ist. Aber zur Übung kann es sinnvoll sein, zuerst ein grundlegendes Verständnis von Arrays und Zeigern zu haben.
-
Danke schön! Bedeutet das, dass ich auch nicht so leicht überprüfen kann, ob zwei Arrays gleich groß sind?
Vielen Dank
lg, freakC++
-
freakC++ schrieb:
Hallo zusammen,
gibt es eine Möglichkeit bei einer Arrayübergabe die Größe des Arrays herauszufinden? Ich will keine STL. Ich erinnere mich an einen ähnlichen Thread, doch ich finde ihn nicht mehr. Ich möchte nämich nicht immer "n" mitübergeben.edit: Ich könnte dem letztten ELemnt einen Schlusswert zuweisen, doch gibt es auch etwas anständiges?

Vielen Dank für eure Hilfe
lg, freakC++Deine Idee funktioniert nicht. Wie willst du das letzte Element rausfinden, wenn du nicht weißt, wie groß es überhaupt ist?
So würds gehen, aber na ja...
#include <iostream> #define ARRAY_CREATE(t, n, s) t n[s + 1]; n[0] = (s); #define ARRAY_SIZE(n) (*n) #define ARRAY_FOREACH(n, i) for (int i = 1; i < ARRAY_SIZE(n) + 1; ++i) void func(int *x); int main() { ARRAY_CREATE(int, x, 10); ARRAY_FOREACH(x, i) { x[i] = i; } func(x); } void func(int *x) { int size = ARRAY_SIZE(x); ARRAY_FOREACH(x, i) { std::cout << i << std::endl; } }
-
#include <iostream> #include <string> #include <ctime> template<typename T> class Array { private: T *myArr; unsigned int mySize; public: Array(unsigned int size) : mySize(size) { myArr = new T[mySize]; } ~Array() { delete[] myArr; } T &getAt(unsigned int pos) { return myArr[pos]; } unsigned int getSize() const { return mySize; } }; template<typename T> void func(Array<T>& arr) { std::cout << "\nSize: " << arr.getSize() << "\n" << std::endl; for (unsigned int i = 0; i < arr.getSize(); ++i) { std::cout << i << ": " << arr.getAt(i) << std::endl; } } int main() { Array<std::string> arr(10); arr.getAt(0) = "lol"; arr.getAt(9) = "bla"; func(arr); Array<int> arr2(5); srand(time(NULL)); for (unsigned int i = 0; i < arr2.getSize(); ++i) { arr2.getAt(i) = rand() % 100; } func(arr2); }
-
Wenn man schon
std::vectornachbaut, sollte man auch wichtige Dinge wie Kopierkonstruktor und Zuweisungsoperator nicht vergessen...Im Übrigen gibts für statische Arrays den Wrapper
std::tr1::array. Ich verwende bei eigenen Projekten nur noch den, Arrays sind mir zu unkomfortabel und riskant.
-
baut der compiler nicht irgendwie vor oder hinter das letzte element die größe, damit delete[] funktionieren kann? könnte man nicht das für sich nutzen?
-
määägic schrieb:
baut der compiler nicht irgendwie vor oder hinter das letzte element die größe, damit delete[] funktionieren kann? könnte man nicht das für sich nutzen?
Es ist auf jeden Fall undefiniert und somit bist du gleichweit, wie wenn dus nicht benutzt..
-
Nein, da sowas implementierungsabhängig ist und vom C++-Standard nicht garantiert wird. Bei statischen Arrays ginge das sowieso nicht.
Ist nicht böse gemeint, aber die drei bisherigen Vorschläge, nur um sich nicht mit vorhandenen Standardmitteln wie der STL zu beschäftigen, halte ich für etwas fragwürdig. Zumal die hier gebrachten Codestücke in meinen Augen weder elegant noch besonders benutzerfreundlich oder sicher sind.
-
#include <iostream> #include <cassert> template<typename T> class Array { private: T *myArr; unsigned int mySize; public: Array(unsigned int size) { init(size); } Array(const Array& arr) { init(arr.getSize()); for (unsigned int i = 0; i < mySize; ++i) { myArr[i] = arr[i]; } } ~Array() { cleanup(); } void operator=(Array &arr) { cleanup(); init(arr.getSize()); for (unsigned int i = 0; i < mySize; ++i) { myArr[i] = arr[i]; } } T operator[](unsigned int n) const { assert(n < mySize); return myArr[n]; } T& operator[](unsigned int n) { assert(n < mySize); return myArr[n]; } unsigned int getSize() const { return mySize; } private: void init(unsigned int size) { mySize = size; myArr = new T[mySize]; } void cleanup() { if (myArr) { delete[] myArr; } } }; int main() { Array<int> test(2); test[0] = 100; test[1] = 200; Array<int> copied = test; Array<int> nothing(12); nothing = test; std::cout << test[0] << ", " << test[1] << std::endl; std::cout << copied[0] << ", " << copied[1] << std::endl; std::cout << nothing[0] << ", " << nothing[1] << std::endl; }Sinnvoll ist es natürlich nicht, aber habe ich auch nie behauptet

-
Halte ich auch nicht für sehr sinnvoll. Ausser gerade mal eben für ein wenig Übung. Aber du ignorierst z.B Exceptionsicherheit.
Dann ist dein cleanup überflüssig, wenn du anständig mit Initialisierungslisten arbeitest.
Dazu kommt noch etwas prinzipielles. Anscheinend ist dein Array irgendwie mit einer festen Grösse, aber du machst es trotzdem dynamisch und implementierst einen Zuweiungsoperator. Wenn ich also z.B ein Element noch zusätzlich einfügen will zur Laufzeit, dann muss ich dafür ein Array erstellen, dass 1 Grösser ist und das meinem ursprünglichen zuweisen.. Nicht sehr konsequent.

-
drakon schrieb:
Dann ist dein cleanup überflüssig, wenn du anständig mit Initialisierungslisten arbeitest.
Kannst du das mal beispielhaft demonstrieren? Mich hat allgemein geärgert, dass ich gewissermaßen Redundanzen drinne habe. So macht der Kopierkonstruktor zunächst exakt das selbe, wie der Konstruktor. Aber der wird ja dann nicht aufgerufen, also muss ich im Kopierkonstruktor erneut den Code schreiben...
Wäre cool, wenn du mir da an der Stelle etwas weiterhelfen könntest

-
Naja. Also das zerstören ist nicht überflüssig, aber die Überprüfung auf 0.
Wenn der Konstruktor erfolgreich aufgerufen wurde, dann solltest du davon ausgehen können, dass auch die Allokation geklappt hat.Und wenn du dann den Zuweigungsoperator auch nocht mit z.B dem Copy/Swap Idiom beglückst, dann hast du da auch genügend Exceptionsicherheit, dass im Destruktor immernoch alles definiert ist. (Mit dem Idiom hast du dann auch die Redundanz weg).
-
Im Bezug auf Exceptionsicherheit würde ich die Klasse vielleicht etwa folgendermassen implementieren. Dazu habe ich das Copy&Swap-Idiom verwendet. Ich hoffe, ich habe jetzt keine gröberen Fehler gemacht...
template<typename T> class Array { public: Array(unsigned int Size) : MySize(size) , MyArr(new T[Size]) { } Array(const Array& Origin) : MySize(Origin.MySize) , MyArr(new T[Origin.MySize]) { std::copy(Origin.MyArr, Origin.MyArr + Origin.MySize, MyArr); } ~Array() { delete[] MyArr; } Array& operator= (const Array& Origin) { Array Temp(Origin); swap(Temp); return *this; } void swap(Array& Other) { std::swap(MyArr, Other.MyArr); std::swap(MySize, Other.MySize); } private: unsigned int MySize; T* MyArr; };Ausserdem würde ich eine
operator[]-Überladung schreiben, die eine Const-Referenz (und keine Kopie) zurückgibt. Und den Speicher würde ich womöglich nicht gleich mit default-konstruierten Objekten initialisieren (kommt jedoch auf den Anwendungsfall an).
-
zuzuzuzu schrieb:
...
Nexus schrieb:
...
Das ist zwar auch interessant ... belegt aber im Prinzip nur, dass die Weigerung, std::vector (oder andere Elemente der StdLib) zu verwenden, fast immer dazu führt, dass man diese Dinge selbst nachprogrammiert - und das üblicherweise schlechter (aufwendiger, fehleranfälliger, unflexibler, langsamer, schlechter zu bedienen, .... ).

Merke: Die Jungs, die die StdLib schreiben, können schon was.

Gruß,
Simon2.
-
Simon2 schrieb:
Das ist zwar auch interessant ... belegt aber im Prinzip nur, dass die Weigerung, std::vector (oder andere Elemente der StdLib) zu verwenden, fast immer dazu führt, dass man diese Dinge selbst nachprogrammiert - und das üblicherweise schlechter (aufwendiger, fehleranfälliger, unflexibler, langsamer, schlechter zu bedienen, .... ).

Ich habe meinen Code auch weniger mit dem Ziel geschrieben, diesen als Array-Klasse einzusetzen, sondern eher um zuzuzuzu eine exceptionsichere Implementierung aufzuzeigen, da er danach gefragt hat.
Ich kann mich deiner Meinung völlig anschliessen, das habe ich ja oben schon geschrieben:
Nexus schrieb:
[...] die drei bisherigen Vorschläge, nur um sich nicht mit vorhandenen Standardmitteln wie der STL zu beschäftigen, halte ich für etwas fragwürdig. Zumal die hier gebrachten Codestücke in meinen Augen weder elegant noch besonders benutzerfreundlich oder sicher sind.