[Gelöst] Speicherloch bei std::string

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

[Gelöst] Speicherloch bei std::string

Beitrag von eXile »

Hallo,

ich habe durch Zufall das folgende Verhalten auf einem Linux-System mit Valgrind gemacht. Natürlich zuerst ein Minimalbeispiel:

Code: Alles auswählen

#include <string>

class myBase {};

class myDerived : public myBase
{
public:
	std::string str;
};

class myOtherClass
{
public:
	myBase *b;
};

int main(int argc, char* argv[])
{
        myOtherClass o;
	myDerived* d = new myDerived;
	o.b = d;

	d->str = "asd";

	// Eines von beiden auskommentieren
	//delete o.b; // Valgrind meldet "16 bytes in 1 blocks are definitely lost"
	//delete d; // Valgrind meldet "0 errors from 0 contexts"

        return 0;
}
Die beiden zuletzt kommentierten Zeilen enthalten dabei die Valgrind-Ausgabe ... im ersten Fall ein Speicherloch, im zweiten Fall keines.

Fragen:
  1. 1. Habe ich einen grundsätzlichen programmiertechnischen Fehler begangen?
    2. Falls nein bei (1): Liegt es an der von mir verwendeten libstdc++?
    3. Fall ja bei (1): Müsste es nicht bei beiden Fällen zu einem Speicherloch kommen?
    4. Ist das Verhalten bei euch reproduzierbar?
Ich bin für alle Antworten dankbar, denn irgendwie habe ich das starke Gefühl, grade extrem große Tomaten auf den Augen zu haben ;)

Edit:
Ich hatte wirklich Tomaten auf den Augen. Der Destruktor von MyBase muss natürlich virtuell sein, damit die Speicherfreigabe klappt. (Normalerweise mache ich den Destruktor immer virtuell, aber da das Beispiel ursprünglich aus einer ganzen Batterie von mehreren Hundert "Mini-Klassen" stammt, hatten die gar keinen Destruktor, und ich hab darum das Thema der virtuellen Destruktoren völlig vernachlässigt.) Sorry für den Thread ;)
Zuletzt geändert von eXile am 07.09.2010, 22:12, insgesamt 2-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Speicherloch bei std::string

Beitrag von Krishty »

Dein Fehler – mach den D’tor virtual.

Wenn du einen Zeiger auf die Basisklasse deletest, kann delete ja nicht wissen, dass der Destruktor der abgeleiteten Klasse aufgerufen werden soll, solange er nicht virtual ist. Es ruft nur den D’tor der Basisklasse auf, die keinen String enthält und ergo auch keinen String freigibt.

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: [Gelöst] Speicherloch bei std::string

Beitrag von eXile »

Ja, wie bereits im obigen Edit gesagt - ich hatte wirklich Tomaten auf den Augen. Aber einmal im Jahr darf das einem wohl auch mal passieren (wie peinlich!) :P
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [Gelöst] Speicherloch bei std::string

Beitrag von Gene »

möglicherweise peinlich für dich, aber sehr gut für mich.

Mir war nicht bewusst, das ich noch so expliziet auf die D'Tore achten muss, wenn ich garkeine nutzte.

Was mich noch wundert ist, warum gibt es bei dem einem delete keinen Speicherverlust?
Oder wurde das einfach nicht von Valgrind erkannt?
Benutzeravatar
Biolunar
Establishment
Beiträge: 154
Registriert: 27.06.2005, 17:42
Alter Benutzername: dLoB

Re: [Gelöst] Speicherloch bei std::string

Beitrag von Biolunar »

d ist vom myDerived*, also wird auch das myDerived-Objekt gelöscht. Wenn er nur einen Zeiger auf die Basisklasse löscht, wird auch nur die Basisklasse gelöscht, nicht aber die Abgeleitete Klasse.
Benutzeravatar
Krishty
Establishment
Beiträge: 8245
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Gelöst] Speicherloch bei std::string

Beitrag von Krishty »

Gene hat geschrieben:Was mich noch wundert ist, warum gibt es bei dem einem delete keinen Speicherverlust?
Wenn der D’tor nicht virtual ist, wird delete d (entgegen dem, was der Programmierer will) etwa so verarbeitet:
d ist vom Typ myBase. Also rufe myBase::~myBase() auf d auf
• Gib den Speicherplatz an der Adresse von d frei
myDerived::~myDerived() wird nie aufgerufen, also wird der String auch nie freigegeben.

Mit einem virtuellen D’tor sieht es hingegen so aus:
d ist vom Typ myBase. myBase hat einen virtuellen D’tor, also muss im vftable von d nachgeschlagen werden, welcher D’tor aufzurufen ist.
• Im vftable ist myDerived::~myDerived() als D’tor eingetragen. Rufe diese Funktion auf d auf (jetzt wird d.str freigegeben)
• Rufe myBase::~myBase() auf d auf (das geschieht eigentlich noch innerhalb von myDerived::~myDerived(), ist aber der Klarheit halber nochmal aufgeführt)
• Gib den Speicherplatz an der Adresse von d frei

Ein ganz fieser Fehler, wenn man den Umstand nicht kennt. Darum den D’tor von Basisklassen immer virtual deklarieren … es sei denn, man weiß, was man tut (ich spare gern ein paar KiB und Instruktionen, indem ich vftables bei Polymorphen Typen, deren Verwendung aber sicher an einen bestimmten Typ gebunden ist, weglasse *in die Luft guck*)
Zuletzt geändert von Krishty am 23.04.2010, 18:58, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [Gelöst] Speicherloch bei std::string

Beitrag von Aramis »

Im Allgemeinen gilt die Regel, dass polymorphe Typen stets einen virtellen d’tor besitzen sollten. Als polymorph gelten in C++ benutzerdefinierte Typen mit mindestens einem virtuellen Member (sie sind daran zu erkennen dass ein dynamic_cast auf ihnen erlaubt ist). Nicht-polymorphe Typen sollten *bewusst* keinen virtuellen d’tor definieren, weil sie sonst ja keine nicht-polymorphen Typen mehr waeren :-)

(eXile’s Beispiel ist kein polymorpher Typ, aber im wirklichen Leben haette er wohl sicherlich einen Satz virtueller Member, denn sonst kann auf das Verhalten der ableitenden Klasse ja gar nicht mehr zugegriffen werden).
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [Gelöst] Speicherloch bei std::string

Beitrag von Gene »

Danke für die Antwort. Ich hab nicht daran gedacht das d und o.b Unterschiedliche Datentypen sind. (Wobei es hier ja genau darum geht)

mfg
Gene
Antworten