Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++ Quizfrage, welche Antwort ist richtig?

8 views
Skip to first unread message

Heinz Saathoff

unread,
Feb 10, 2010, 9:50:44 AM2/10/10
to
Moin,

ich habe mich am C++ Quiz auf http://www.netrino.com/ versucht.
Dort gibt es eine Frage, bei der meine Antwort von der
erwarteteb L�sung abweicht. Ich bin aber der �berzeugung,
dass meine Antwort richtig ist. Wer hat nun recht?


Q: What will happen in the following code after the=20
function foo() returns?

//======== Quiz Code ==========
class base
{
public:
base() { }
~base() { }
};

class derived : public base
{
private:
int *p_pi_values;

public:
derived() : p_pi_values(new int[100]) { }
~derived() { delete [] p_pi_values; }
};

void foo(void)
{
derived *p_derived = new derived();
base *p_base = p_derived;

// Do some other stuff here.

delete p_base;
}
//======= End Quiz Code ============

M�gliche Antworten:
1: The integer array is guaranteed to be properly deleted
2: The integer array will not be properly deleted
3: Nothing. This will not compile
4: The behavior is undefined

Nach meiner Meinung war die Antwort 2 richtig, da der
Destruktor von Base nicht virtuell ist.
Die angeblich richtige Antwort ist aber 4.
Warum sollte das Verhalten undefiniert sein?


- Heinz

Stefan Große Pawig

unread,
Feb 10, 2010, 11:53:36 AM2/10/10
to
Hallo Heinz!

Heinz Saathoff <news...@arcor.de> writes:
> Mögliche Antworten:


> 1: The integer array is guaranteed to be properly deleted
> 2: The integer array will not be properly deleted
> 3: Nothing. This will not compile
> 4: The behavior is undefined
>
> Nach meiner Meinung war die Antwort 2 richtig, da der
> Destruktor von Base nicht virtuell ist.
> Die angeblich richtige Antwort ist aber 4.
> Warum sollte das Verhalten undefiniert sein?

Weil das der Standard so sagt. ;-)

Aus ISO/IEC 14882:1998, 5.3.5 Delete:
---
3 In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a
base class of the operand’s dynamic type and the static type shall
have a virtual destructor or the behavior is undefined.
---

-Stefan

Björn Hendriks

unread,
Feb 10, 2010, 1:00:25 PM2/10/10
to
Heinz Saathoff wrote:

> ich habe mich am C++ Quiz auf http://www.netrino.com/ versucht.
> Dort gibt es eine Frage, bei der meine Antwort von der

> erwarteteb Lösung abweicht. Ich bin aber der Überzeugung,


> dass meine Antwort richtig ist. Wer hat nun recht?
>
>
> Q: What will happen in the following code after the=20
> function foo() returns?
>
> //======== Quiz Code ==========
> class base
> {
> public:
> base() { }
> ~base() { }
> };
>
> class derived : public base
> {
> private:
> int *p_pi_values;
>
> public:
> derived() : p_pi_values(new int[100]) { }
> ~derived() { delete [] p_pi_values; }
> };
>
> void foo(void)
> {
> derived *p_derived = new derived();
> base *p_base = p_derived;
>
> // Do some other stuff here.
>
> delete p_base;
> }
> //======= End Quiz Code ============
>

> Mögliche Antworten:


> 1: The integer array is guaranteed to be properly deleted
> 2: The integer array will not be properly deleted
> 3: Nothing. This will not compile
> 4: The behavior is undefined
>
> Nach meiner Meinung war die Antwort 2 richtig, da der
> Destruktor von Base nicht virtuell ist.
> Die angeblich richtige Antwort ist aber 4.
> Warum sollte das Verhalten undefiniert sein?

Ich hätte auch auf 2 getippt, aber nach längerer Suche im Standard habe ich
nun die passende Stelle gefunden in 5.3.5, Absatz 3:

In the first alternative (delete object), if the static type of the operand
is different from its dynamic type, the static type shall be a base class of
the operand’s dynamic type and the static type shall have a virtual
destructor or the behavior is undefined.

Statischer und dynamischer Typ beim Delete unterscheiden sich, aber wir
haben keinen virtuellen Destruktor, also ist es undefiniert, wobei
derived::p_pi_values dabei gar keine Rolle spielt.

Nach dem was ich hier und anderswo so gelesen habe, könnte der Grund für
diese Regelung darin liegen, dass der Compiler eigenen Code zum Aufräumen
von Objekten einfach an die Destruktoren anhängen darf und sich dann darauf
verlassen kann, dass dieser auch tatsächlich aufgerufen wird. Beim obigen
Beispiel wird der Destruktor von p_derived jedoch nie aufgerufen, so dass
ein solcher Compiler Probleme bekommen könnte und zwar ganz unabhängig
davon, ob derived::p_pi_values ordentlich vernichtet wird.

Gruß
Björn

Stefan Große Pawig

unread,
Feb 10, 2010, 4:51:04 PM2/10/10
to
Hallo Björn,

Björn Hendriks <he...@nurfuerspam.de> writes:
> Nach dem was ich hier und anderswo so gelesen habe, könnte der Grund für
> diese Regelung darin liegen, dass der Compiler eigenen Code zum Aufräumen
> von Objekten einfach an die Destruktoren anhängen darf und sich dann darauf
> verlassen kann, dass dieser auch tatsächlich aufgerufen wird. Beim obigen
> Beispiel wird der Destruktor von p_derived jedoch nie aufgerufen, so dass
> ein solcher Compiler Probleme bekommen könnte und zwar ganz unabhängig
> davon, ob derived::p_pi_values ordentlich vernichtet wird.

Interessant wird es bei Mehrfachvererbung, z.B.:

---- %< ---- SNIP ---- %< ----
struct A
{
int a;
~A() {}
};

struct B
{
int b;
~B() {}
};

struct C : public A, public B
{
int c;
~C() {}
};

int main()
{
C* pC = new C;
B* pB = pC;

delete pB; // RUMMS (jedenfalls mit gcc-4.3.2)
}
---- >% ---- SNAP ---- >% ----

Ohne virtuellen Destruktor geht das Freigeben des Speichers in die Hose,
da von B::~B() die "richtige" Startadresse des Gesamtobjekts nicht
ermittelt werden kann (weil in B::~B() keine Informationen darüber
vorliegen, dass dieses B-Objekt durch eine Vererbungshierarchie in ein
größeres Gesamtobjekt eingebettet ist).

-Stefan

Heinz Saathoff

unread,
Feb 11, 2010, 4:43:38 PM2/11/10
to
Stefan Gro�e Pawig schrieb:

> [Beispiel Mehrfachvererbung und delete]


>
>Ohne virtuellen Destruktor geht das Freigeben des Speichers in die Hose,
>da von B::~B() die "richtige" Startadresse des Gesamtobjekts nicht

>ermittelt werden kann (weil in B::~B() keine Informationen dar�ber


>vorliegen, dass dieses B-Objekt durch eine Vererbungshierarchie in ein

>gr��eres Gesamtobjekt eingebettet ist).

Eigendlich d�rfte deshalb die in
http://www.dietmar-kuehl.de/mirror/c++-faq/freestore-mgmt.html (16.9)
gemachte Umsetzung von delete nicht stimmen. Dort wird

delete p;
als �quivalent zu
if(p) {
p->~P();
operator delete(p);
}
beschrieben.

Bei dieser Umsetzung findet aber keinerlei Anpassung des Pointers p
statt. Wenn die in der FAQ genannte Umsetzung gelten w�rde, w�re meine
Antort 2 auf die Quizfrage OK gewesen. Es findet aber, wie das Beispiel
Mehrfachvererbung zeigt, etwas mehr statt.


- Heinz

SG

unread,
Feb 10, 2010, 12:02:54 PM2/10/10
to
On 10 Feb., 15:50, Heinz Saathoff wrote:
> >
> > ich habe mich am C++ Quiz auf  http://www.netrino.com/versucht.
> > Dort gibt es eine Frage, bei der meine Antwort von der
> > erwarteteb Lösung abweicht. Ich bin aber der berzeugung,

> > dass meine Antwort richtig ist. Wer hat nun recht?
> >
> > Q: What will happen in the following code after the
> >    function foo() returns?

[schnipp]

> > Mögliche Antworten:


> >   1: The integer array is guaranteed to be properly deleted
> >   2: The integer array will not be properly deleted
> >   3: Nothing. This will not compile
> >   4: The behavior is undefined
> >
> > Nach meiner Meinung war die Antwort 2 richtig, da der
> > Destruktor von Base nicht virtuell ist.
> > Die angeblich richtige Antwort ist aber 4.
> > Warum sollte das Verhalten undefiniert sein?

Das steht nun einmal so im Standard. Ich selbst habe auch keine Kopie
davon. Es gibt aber einen sehr frühen C++0x Entwurf, N1804 [1], der
größtenteils Deckungsgleich mit dem C++03 Standard sein sollte. Dort
steht in 5.3.5/3 (Kapitel 5.3.5, Abschnitt 3):

"... if the static type of the operand is different from its dynamic


type, the static type shall be a base class of the operand's dynamic
type and the static type shall have a virtual destructor or the

behaviour is undefined..."

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1804.pdf

Gruß,
SG


Björn Hendriks

unread,
Feb 12, 2010, 5:33:24 AM2/12/10
to
Heinz Saathoff wrote:

> Stefan Große Pawig schrieb:


>
>> [Beispiel Mehrfachvererbung und delete]
>>
>>Ohne virtuellen Destruktor geht das Freigeben des Speichers in die Hose,
>>da von B::~B() die "richtige" Startadresse des Gesamtobjekts nicht

>>ermittelt werden kann (weil in B::~B() keine Informationen darüber


>>vorliegen, dass dieses B-Objekt durch eine Vererbungshierarchie in ein

>>größeres Gesamtobjekt eingebettet ist).
>
> Eigendlich dürfte deshalb die in


> http://www.dietmar-kuehl.de/mirror/c++-faq/freestore-mgmt.html (16.9)
> gemachte Umsetzung von delete nicht stimmen. Dort wird
>
> delete p;

> als äquivalent zu


> if(p) {
> p->~P();
> operator delete(p);
> }
> beschrieben.
>
> Bei dieser Umsetzung findet aber keinerlei Anpassung des Pointers p

> statt. Wenn die in der FAQ genannte Umsetzung gelten würde, wäre meine


> Antort 2 auf die Quizfrage OK gewesen. Es findet aber, wie das Beispiel
> Mehrfachvererbung zeigt, etwas mehr statt.

Wenn ich den Standard richtig lese, ist obiges allenfalls eine mögliche
Implementation. Laut Standard muss delete lediglich den passenden Destruktor
aufrufen und dann den Speicher freigeben, der zu dem übergebenen Pointer
gehört, wobei der Pointer von einem vorherigen Aufruf von new stammen muss
(außer es ist der Null-Pointer, bei dem delete gar keinen Effekt haben
darf).

Woher delete nun weiß welcher und wieviel Speicher freizugeben ist, bleibt
demnach der Implementation überlassen. Der Compiler kann es also aus dem Typ
des übergebenen Pointers ableiten, muss es aber nicht. Alternativ könnte new
auch in eine Tabelle schreiben, wieviel Speicher zu einem Pointer allokiert
wurde und delete sich danach richten.

Oder liege ich mit dieser Interpretation des Standards falsch?

Gruß
Björn

Falk Tannhäuser

unread,
Feb 13, 2010, 3:10:56 PM2/13/10
to
Am 11.02.2010 22:43, schrieb Heinz Saathoff:
> Eigentlich d�rfte deshalb die in

> http://www.dietmar-kuehl.de/mirror/c++-faq/freestore-mgmt.html (16.9)
> gemachte Umsetzung von delete nicht stimmen. Dort wird
>
> delete p;
> als �quivalent zu
> if(p) {
> p->~P();
> operator delete(p);
> }
> beschrieben.
>
> Bei dieser Umsetzung findet aber keinerlei Anpassung des Pointers p
> statt. Wenn die in der FAQ genannte Umsetzung gelten w�rde, w�re meine
> Antwort 2 auf die Quizfrage OK gewesen. Es findet aber, wie das Beispiel

> Mehrfachvererbung zeigt, etwas mehr statt.

Ja, in der FAQ ist die Sache wohl sehr vereinfacht dargestellt - obiger Ersatzcode trifft nur zu, solange kein Polymorphismus
ins Spiel kommt. Ein weiterer Fall, der ohne virtuellen Destruktor wahrscheinlich kaum korrekt behandelt wird, ist die
�berladung des "operator delete" (i.d.R. passend zu einem ebenfalls �berladenen "operator new") in der abgeleiteten Klasse. Der
richtige "operator delete" muss ja abh�ngig vom dynamischen Typ des aufzur�umenden Objekts ausgew�hlt werden.

MfG
Falk

Stefan Große Pawig

unread,
Feb 12, 2010, 1:13:25 PM2/12/10
to
Hallo Heinz!

Heinz Saathoff <news...@arcor.de> writes:
> Eigendlich dürfte deshalb die in


> http://www.dietmar-kuehl.de/mirror/c++-faq/freestore-mgmt.html (16.9)
> gemachte Umsetzung von delete nicht stimmen. Dort wird
>
> delete p;

> als äquivalent zu


> if(p) {
> p->~P();
> operator delete(p);
> }
> beschrieben.

In der FAQ steht ja auch nur, dass der vom Compiler generierte Code
"functionally similar" (also ähnlich) ist; von "äquivalent" ist dort
nicht die Rede.

Wenn ein virtueller Destruktor vorliegt, greifen hier Mechanismen, die
sich nicht als normaler C++-Code hinschreiben lassen. In diesem Fall
sieht die Prozedur eher so aus:

--- SNIP ---
if (p) {
// neu: Typanpassung
D = dynamic_type(p); // gibt es so nicht
D* d = dynamic_cast<D*>(p);
// wie gehabt:
d->~D();
operator delete(d);
}
--- SNIP ---

Die tatsächliche Umsetzung der beiden neuen Zeilen erfolgt dabei durch
einen Blick in die vtable (oder welchen Mechanismus der Compiler auch
immer dafür vorsieht).

-Stefan

James Kanze

unread,
Feb 12, 2010, 3:54:18 PM2/12/10
to
On Feb 11, 9:43 pm, Heinz Saathoff <newshs...@arcor.de> wrote:
> Stefan Große Pawig schrieb:

[...]
> Eigendlich dürfte deshalb die
> inhttp://www.dietmar-kuehl.de/mirror/c++-faq/freestore-mgmt.html(16.9)


> gemachte Umsetzung von delete nicht stimmen. Dort wird

> delete p;
> als äquivalent zu


> if(p) {
> p->~P();
> operator delete(p);
> }
> beschrieben.

Es geht nur um ein grobes Umschreiben, um den Unterschied
zwischen dem delete-Ausdruck und der operator delete Funktion zu
erleuchten. Es wird nicht behauptet, dass es alle Feinheiten
abdeckt.

> Bei dieser Umsetzung findet aber keinerlei Anpassung des
> Pointers p statt. Wenn die in der FAQ genannte Umsetzung

> gelten würde, wäre meine Antort 2 auf die Quizfrage OK


> gewesen. Es findet aber, wie das Beispiel Mehrfachvererbung
> zeigt, etwas mehr statt.

Freilich. In vielen Implementierung befindet sich der Aufruf des
operator delete-Funktion tatsächlich im Destruktor, da so viel
vom meist abgeleiteter Klasse abhängt. (Denk mal daran, was
passiert, wenn die abgeleitete Klasse einen klassenspezifischen
operator delete enthält, z.B.)

--
James Kanze

0 new messages