C++ Verschiedene Speicheradressen für gleiche Objekte

KingJoshii1000

Lieutenant
Registriert
Apr. 2010
Beiträge
879
Schönen guten Tag,
ich bin akutell dabei, das Vector Template/Klasse ein wenig nachzubauen, um mich intensiv mit Sachen wie Speicherallokation, Klassenoperatoren etc. zu beschäftigen. Nun habe ich um mein Template/Klasse leichter debuggen zu können, eine kleine Funktion geschrieben, welche mir die Größe, Kapazität und jedes Objekt welches sich im Vector befindet auflistet inkl. der Adresse des Objektes und dort liegt auch das Problem. Führe ich meine Funktion aus bekomme ich diese Ausgabe:
0029F78C (Adresse des Vectorobjektes)
0029F6A8 (Adresse des ersten Objektes)
0029F6B4 (Adresse des zweiten Objektes)
0029F6C0 (Adress des dritten Objektes)
Debug MyVector: Size: 3 Capacity: 8
________________________________________________________________________________

0 0029F57C initalisiert
1 0029F57C initalisiert
2 0029F57C initalisiert
3 0029F588 allokiert
4 0029F588 allokiert
5 0029F588 allokiert
6 0029F588 allokiert
7 0029F588 allokiert
Please enter a character to exit
Main.cpp
Code:
#include "MyStuff.h"
using namespace MyStuff;
class ksk{ //Kein Standartkonstruktor
public:
	ksk(int value)
		:i(value){}
	int i;
private:

};
int main(int args_size, char** argc){
	MyVector<ksk>a;
	a.push_back(ksk(1));
	a.push_back(ksk(2));
	a.push_back(ksk(3));
	cout << &a << endl;
	cout << &a[0] << endl;
	cout << &a[1] << endl;
	cout << &a[2] << endl;
	Debug_MyVector<ksk>(a);
	keep_window_open();
}
MyStuff.h
Code:
	/////////////////////////////////////////////////////////////////////////////////////
	template<class T, class A = allocator<T>>class MyVector{
	public:
		MyVector()
			:elem(0), sz(0), space(0){
		}

		void resize(int newsize, T val = T()){
			reserve(newsize);
			for (int i = newsize; i < sz; ++i) alloc.destroy(&elem[i]);
			for (int i = sz; i < newsize; ++i) alloc.construct(&elem[i], val);
			sz = newsize;
		}

		void push_back(T value){
			if (sz == 0)reserve(8);
			else if (sz == space) reserve(sz * 2);
			alloc.construct(&elem[sz], value);
			++sz;
		}

		int size()const{
			return sz;
		}

		int capacity()const{
			return space;
		}

		T operator[](int p){
			return elem[p];
		}

	private:

		T* elem;
		A alloc;
		int sz; //Real used Space of the Container
		int space; //Reserved Space of the Container

		void reserve(int newalloc){
			if (space > newalloc) return;
			T* temp = alloc.allocate(newalloc);
			for (int i = 0; i < sz; ++i){
				alloc.construct(&temp[i], elem[i]);
				alloc.destroy(&elem[i]);
			}
			alloc.deallocate(elem,space);
			elem = temp;
			space = newalloc;
		}
	};
	/////////////////////////////////////////////////////////////////////////////////////
	template<class T>void Debug_MyVector(MyVector<T>& MV){
		cout << "Debug MyVector: " << "\t" << "Size: " << MV.size() << " " << "\t" << "Capacity: " << MV.capacity() << endl;
		cout << "________________________________________________________________________________" << endl;
		for (int i = 0; i < MV.size(); ++i){
			cout << i << "\t" << &MV[i] << "\t" << "initalisiert" << endl;
		}
		for (int i = MV.size(); i < MV.capacity(); ++i){
			cout << i << "\t" << &MV[i] << "\t" << "allokiert" << endl;
		}
	}
Wie oben erwähnt befindet ist dies ein stark vereinfachter Vector, jedoch wollte ich ihn noch nicht weiter entwickeln, solange dieses Problem noch besteht. Nur wo liegt der Fehler? In der Main.cpp werden wir die augenscheinlich richtigen Adressen geliefert, die Debug-Funktion verlangt eine Referenz auf den Vector somit muss es sich um den gleichen handeln(Pass-by Reference), ich kopiere das Objekt ja nicht (Pass-by Value). Ich würde mich auch über Kritik eurerseits freuen, um mich noch weiter verbessern zu können:).

Mit freundlichen Grüßen:)
 
Du greifst mit MV nicht direkt auf das Array zu, sondern führst diese Funktion aus:

T operator[](int p){
return elem[p];
}

Somit liefert &MV vermutlich die Adresse der Funktion (ich bin mir nicht 100% sicher), aber auf jeden Fall nicht den Speicherort im Array ;)
Wäre ja doofe, wenn du so einfach außerhalb der Klasse einfach auf private Elemente zugreifen könntest. Datenhoheit und so ;)
 
Das Problem ist der Rückgabe-Typ des []-Operators:

Code:
		T operator[](int p){
			return elem[p];
		}
Damit erstellst du ja quasi eine Kopie des Objects elem[p] auf dem Stack und holst dir dann von dieser Kopie die Addresse.

Was du willst - besonders auch, um die bereits gespeicherten Objekte verändern zu können - ist, eine Referenz auf das Objekt zurückzuliefern:
Code:
      T& operator [] (const size_t id)       { return this->elem[id]; }
const T& operator [] (const size_t id) const { return this->elem[id]; }

Eine Referenz ist ja quasi nichts anderes als ein dereferenzierter Zeiger - tatsächlich liefert der *-Operator eines Pointers eine Referenz - daher wirst du nun auch die Speicheraddresse des Objekts bekommen.

Kleine Warnung: Referenzen auf Member zurückliefern ist - abseits von diesem Vector-Szenario, Iteratoren o.ä. - selten eine gute Idee, weil diese ungültig werden, sobald das Carrier-Objekt (in diesem Fall also der Vector selbst) zerstört wird. Da bekommt man teilweise schwer zu findende Probleme, im schlimmsten Fall Sicherheitslücken.

Nebenbei würde ich dringend empfehlen, nicht int als Index-Typen zu nehmen - das kann fatal sein, wenn man den Wertebereich verlässt oder sich plötzlich in Java-Manier um negative Werte kümmern muss. Was du suchst, ist size_t.

Edit: Generell ist das Verwenden von Referenzen - häufig const-Referenzen - bei Parametern von Funktionen sinnvoll. Kleines Beispiel:
Code:
void push_back(T value){ ... }
Wenn ich jetzt schreibe:
Code:
a.push_back(object);
Was passiert dann?
Dann wird erst einmal eine temporäre Kopie von object angelegt, an die Methode übergeben, und dann innerhalb der Methode noch eine Kopie angelegt, die dann letztenendes gespeichert wird.

Ist das sinnvoll? Offensichtlich nicht, immerhin wird das temporäre Objekt nicht verändert.
Kann man das verhindern? Ja.
Code:
void push_back(const T& value){ ... }
Was passiert nun? Die temporäre Kopie verschwindet, weil wir nur eine Referenz auf object übergeben, die dann weitergereicht wird.


Der Vollständigkeit halber kann man sich natürlich noch um Move-Semantik kümmern, aber heb dir das für später auf, wenn der Rest erst einmal verstanden ist. Aber gerade anhand einer solchen Vector-Klasse kann man solche Dinge gut lernen.
 
Zuletzt bearbeitet:
Hallo,
vielen Dank für eure Antworten:).
Ich habe das '&' in der operator[]-Funktion vergessen:(. Sitze an dem Fehler schon zwei Tage und dabei lag es an etwas so einfachem. Nun funktioniert alles perfekt:).
Desweiteren möchte ich mich für eure Kritik bzw. Verbessungsvorschläge bedanken:), werde die Push_Back-Funktion verbessern und ab sofort size_t benutzen.

Mit freundlichen Grüßen:)
 
Zurück
Oben