Forum: PC-Programmierung C++ Problem mit Iterator / Destruktor


von Mit dem Zeh (plus plus) auf dem Schlauch Stehender (Gast)


Angehängte Dateien:

Lesenswert?

ich hab grad ein Knoten im Kopf weil ich ein selbst geschriebenes C++ 
Programm nicht versteh ;)

Der Code ist im Anhang. Die Ausgabe ist bei mir wie folgt:

1
Im Konstruktor: array[] = 1 2 4 8 16
2
3
Methode out():
4
i=0, array[0]=1
5
i=1, array[1]=2
6
i=2, array[2]=4
7
i=3, array[3]=8
8
i=4, array[4]=16
9
10
Destruktor aufgerufen
11
12
Methode out():
13
i=0, array[0]=0
14
i=1, array[1]=2
15
i=2, array[2]=4
16
i=3, array[3]=8
17
i=4, array[4]=16
18
19
Destruktor aufgerufen

Jetzt meine Fragen,
1.) Wieso wird der Destruktor zwei mal aufgerufen? Ich lege ja nur ein 
Objekt der Klasse IntVector an?
2.) Was passiert mit dem ersten Element von array[] zwischen den beiden 
Aufrufen von vector.out() so dass der Wert einmal 1 ist und einmal 0?

: Verschoben durch User
von Peter (Gast)


Lesenswert?

hier wird ein neues Object angelegt

> Iterator it(vector);
vector wird kopiert!

du müsste es mit
1
class Iterator {
2
private:
3
  IntVector* intVector;
4
  unsigned int index;
5
6
public:
7
  Iterator(IntVector&); <<<<<& wichtig!
8
  Iterator(IntVector*);
9
  int* next();
10
};

von Mit dem Zeh (plus plus) auf dem Schlauch Stehender (Gast)


Lesenswert?

Ah danke... kaum macht mans richtig schon gehts :)

von Karl H. (kbuchegg)


Lesenswert?

Peter hat das Problem ja schon identifiziert.

Deine Klasse IntVector ist eine gefährliche Klasse: Sie betreibt 
dynamisches Datenmanagement, hat aber keinen von dir geschriebenen 
Kopierkonstruktor. In dem Fall erzeugt sich der Compiler selbst einen, 
der leider das falsche tut: Er kopiert alle Member, auch den Pointer. 
Und damit hast du 2 Objekte, die auf dieselben new-allokierten Daten 
verweisen. Sobald das erste der beiden Objekte zerstört wird, sind auch 
die Daten futsch. Das übrig gebliebene Objekt greift auf Daten zu, die 
nicht mehr existieren bzw. versucht die dann auch zu deleten.

-> schwerer Fehler

Du hast 2 Möglichkeiten
* entweder einen Kopierkonstruktor und Zuweisungsoperator selber
  schreiben
* Oder die Klasse so absichern, dass von Objekten keine Kopien erzeugt
  werden können.

Letzteres geht so
1
class IntVector {
2
private:
3
  int* array;
4
  unsigned int len;
5
public:
6
  IntVector(unsigned int length);
7
  IntVector();
8
  ~IntVector();
9
  unsigned int size();
10
  int& at(unsigned int i);
11
  void out();
12
  void setElement(unsigned int i, int value);
13
14
  friend class Iterator;
15
16
private:
17
  IntVector( const IntVector& rhs );               // absichtlich nicht implementiert
18
  IntVector& operator=( const IntVector& rhs );    // absichtlich nicht implementiert
19
};

versuchst du jetzt (auch irrtümlich) von einem IntVector eine Kopie zu 
erzeugen, dann klopfen dir entweder Compiler oder Linker auf die Finger.

Sobald du in einer Klasse dynamisch mittels new allokierst, solltest du 
dir angewöhnen, zumindest als Minimallösung die Kopiersperre durch 
private definierte und nicht implementierte CCtor und op= einzurichten. 
Spart viel Kopfzerbrechen.

von Mit dem Zeh (plus plus) auf dem Schlauch Stehender (Gast)


Lesenswert?

Hallo nochmal,

einen Zuweisungsoperator kriege ich anscheinend hin, aber beim 
Copy-Konstruktor habe ich ein Problem:

1
IntVector& IntVector::operator=(IntVector& ivec)
2
{
3
  if(this != &ivec)
4
  {
5
    delete [] array;
6
    array = new int[ivec.size()];
7
    for(unsigned int i=0; i<ivec.size(); i++)
8
      *(array+i) = ivec.at(i);
9
    len = ivec.size();
10
  }
11
  return *this;
12
}
13
14
IntVector::IntVector(const IntVector& rhs)
15
{
16
  array = new int[rhs.size()];
17
  for(unsigned int i=0; i<rhs.size(); i++)
18
    *(array+i) = rhs.at(i);
19
  len = rhs.size();
20
}

Jetzt bekomme ich vom Compiler die Fehlermeldung:
error: passing `const IntVector' as `this' argument of `unsigned int 
IntVector::size()' discards qualifiers

Ohne das "const" kompiliert es zwar, aber wenn ich das weglasse, dann 
wäre es ja kein Copy-Konstruktor mehr? Zu Hülf!

von Karl H. (kbuchegg)


Lesenswert?

Mit dem Zeh (plus plus) auf dem Schlauch Stehender schrieb im Beitrag 
#1738878:

> Jetzt bekomme ich vom Compiler die Fehlermeldung:
> error: passing `const IntVector' as `this' argument of `unsigned int
> IntVector::size()' discards qualifiers


Dein Compiler teilt dir mit, dass du die Memberfunktion size nicht 
aufrufen darfst.
In deinem Copy Contructor hast du mit dem const in der Argumentliste dem 
Aufrufer gegenüber die Zusicherung gemacht, dass du das Argument nicht 
verändern wirst.
Nur: die Memberfunktion size() macht diese Zusicherung nicht! D.h. aus 
Sicht des Aufrufers: er muss damit rechnen, dass sich das Objekt durch 
Aufruf der Funktion size() verändern wird.
Das passt jetzt aber nicht zusammen: Auf der einen Seite macht der Copy 
Constructor die Zusicherung: Ich ändere das Objekt nicht, auf der 
anderen Seite möchte er eine Funktion aufrufen die nicht garantiert, das 
das Objekt nicht verändert wird.

Lösung des Dilemmas:
Da size() das Objekt ja nicht verändern braucht, machen wir die Funktion 
einfach const. Dann sind alle glücklich
1
class IntVector {
2
3
...
4
  unsigned int size() const;
5
...
6
};

und natürlich auch in der Implementierung das const noch anfügen
1
unsigned int IntVector::size() const
2
{
3
  return len;
4
}


PS:
Auch im Op= ist das Argument eine const Referenz.
1
IntVector& IntVector::operator=(const IntVector& ivec)

Das man einen Op= so nicht schreibt, weil er nicht exception safe ist, 
lass ich erst mal unkommentiert. Das wird zuviel auf einmal.

von Peter (Gast)


Lesenswert?

> ivec.size()

ist die methode als const declariert?

von Mit dem Zeh (plus plus) auf dem Schlauch Stehender (Gast)


Lesenswert?

Ah, anscheinend muss ich alles und jeden "const" machen, also alle 
Elementfunktionen die ich im Copy-Konstruktor verwenden will... hmpf. 
Kann sich der Compiler das nicht denken? :)

von Karl H. (kbuchegg)


Lesenswert?

Mit dem Zeh (plus plus) auf dem Schlauch Stehender schrieb im Beitrag 
#1738893:
> Ah, anscheinend muss ich alles und jeden "const" machen, also alle
> Elementfunktionen die ich im Copy-Konstruktor verwenden will... hmpf.

Nein.
Du sollst alle Memberfunktionen, die das Objekt nicht verändern, als 
const Funktionen machen! Unabhängig davon, ob sie im Copy Constructor 
verwendet werden oder nicht.

von Peter (Gast)


Lesenswert?

du könntest auch auf private sachen ohne Methode zugreifen, braucht also 
die Methode size nicht zwingend.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.