 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Guest
|
Posted: Thu Apr 19, 2007 1:06 pm Post subject: Zugriff auf unvollständig kosntruiertes Objekt erlaubt stand |
|
|
Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
Beispiel:
f greift auf das globale Objekt a zu, bevor das globale Objekt a
fertig konstruiert worden ist:
#include <iostream>
void f();
class A
{
public:
A()
{
m=5;
f();
}
void g()
{
std::cout << "m=" << m << std::endl;
}
private:
int m;
};
static A a;
void f()
{
a.g();
}
int main()
{
return 0;
} |
|
| Back to top |
|
 |
Marcel Müller Guest
|
Posted: Fri Apr 20, 2007 12:33 am Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
zei2006q1 (AT) liwest (DOT) at wrote:
| Quote: | Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
|
Die entscheidende Frage ist /wie/.
| Quote: | Beispiel:
f greift auf das globale Objekt a zu, bevor das globale Objekt a
fertig konstruiert worden ist:
#include <iostream
void f();
class A
{
public:
A()
{
m=5;
f();
}
void g()
{
std::cout << "m=" << m << std::endl;
}
private:
int m;
};
static A a;
void f()
{
a.g();
}
int main()
{
return 0;
}
|
In diesem Beispiel wird es definiertes Verhalten sein, da a::m bereits
konstruiert ist. Und das liegt nicht daran, dass vor dem Aufruf von f()
m=5 steht, sondern daran, dass vor dem Eintritt in den Konstruktor von
A() /alle/ Basisklassen und Elemente fertig konstruiert sind und dass es
nur ein statisches Objekt von A gibt. Dass m nach der eben genannten
Konstruktion erstmal einen undefinierten Wert hat, ist eine Eigenheit
des POD-Typs int, die in diesem Zusammenhang keine Rolle spielt.
Wenn hingegen mehr als ein statisches Objekt von A existiert, wird es
eng. Da die Initialisierungsreihenfolge derselben nur in bestimmten
Ausnahmefällen definiert ist, hat man schnell undefiniertes Verhalten.
Von der Sache her ist es natürlich etwas unlogisch, dass der Konstruktor
jedes beliebigen A eine Methode eines im allgemeinen anderen A aufruft.
Dabei verliert man i.A. die Threadsicherheit auf Instanzebene - also
verschiedene Threads dürfen nicht unsynchronisiert mit /verschiedenen,/
privaten Instanzen von A arbeiten. Aber das ist ein anderes Thema. Die
Eigentümlichkeit mag sich aber auch mit dem auf das wesentliche
Reduzierten Beispiel ergeben haben. Eine statische Methode g() hätte ich
eher verstanden.
Problematischer wird die Sache, wenn man vor dem Eintritt in den
Konstruktor auf die eigene Klasse zugreift. Z.B. so:
class A : public B
{public:
A() : B(this) { ... }
...
};
Und jetzt B::B() irgendwas mit A treibt.
(Oder analog für Member von A.)
Das gibt je nach Compiler denn auch schon mal eine Warnung.
Es ist natürlich nicht damit zu rechnen, dass A an jeder beliebigen
Stelle des Konstruktors schon hinreichend lebensfähig ist, als dass alle
Methoden von A sich sinnvoll verhalten. Es obliegt aber im Ermessen der
Programmierers dafür zu sorgen, dass das gut geht. Nichts anderes wäre
passiert, wenn A::A() die Methode A::g() bzw. a.g() direkt aufgerufen
hätte - was logischerweise erstmal erlaubt ist. Ob dabei noch eine
andere Funktion dazwischen liegt, ist zweitrangig.
In jedem Fall ist es aber ein nicht sonderlich gut lesbarer
Programmierstil, wenn der Konstruktor von A die Initialisierungsprozedur
an Methoden außerhalb von A aus der Hand gibt.
Außerdem besteht auch ein besonderes Risiko, wenn die zu diesem
Zeitpunkt verwendeten Methoden von A virtuell sind und in Wirklichkeit
eine von A abgeleitete Klasse konstruiert werden soll. Da die
abgeleitete Klasse noch nicht konstruiert ist, können nämlich noch
keinerlei von ihr überladene Methoden aufgerufen werden. Und im
Spezialfall, dass die Methode in A sogar abstrakt ist, bekommt man dann
ganz schnell mal ein "virtual pure function called". Aber auch das gilt
auch, wenn A::A() die Methoden direkt aufruft. Es ist also keine
Besonderheit dieses Falls. Es ist nur weniger ersichtlich.
Also wie gesagt, kein doller Programmierstil, aber wenn man es richtig
macht, definiertes Verhalten. Und es gibt einige Gründe, wo eine solche
Lösung auch durchaus vertretbar ist. Beispielsweise, wenn der
Konstruktor etwas aufruft, was eine Callback-Methode (von A) benötigt.
In dem Fall kann der Konstruktor aber noch kontrollieren, welche
Methoden von A aufgerufen werden, und es ist deshalb auch kein
schlechter Stil.
Marcel |
|
| Back to top |
|
 |
Florian Kesseler Guest
|
Posted: Fri Apr 20, 2007 2:38 am Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
zei2006q1 (AT) liwest (DOT) at schrieb:
| Quote: | Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
Da der Standard nichts über Parallelität aussagt, ist es zwar erlaubt |
(weil es eben nicht verboten ist), wird aber normalerweise nur zu
Problemen führen.
Bei Programmen, die in nur einem Thread laufen, kann das nicht
passieren, deshalb beachtet der Standard es nicht. |
|
| Back to top |
|
 |
Thomas Mang Guest
|
Posted: Fri Apr 20, 2007 11:29 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
Marcel Müller wrote:
| Quote: | zei2006q1 (AT) liwest (DOT) at wrote:
Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
Die entscheidende Frage ist /wie/.
|
Was in 3.8/6 näher erläutert wird.
| Quote: |
Beispiel:
f greift auf das globale Objekt a zu, bevor das globale Objekt a
fertig konstruiert worden ist:
#include <iostream
void f();
class A
{
public:
A()
{
m=5;
f();
}
void g()
{
std::cout << "m=" << m << std::endl;
}
private:
int m;
};
static A a;
void f()
{
a.g();
}
int main()
{
return 0;
}
In diesem Beispiel wird es definiertes Verhalten sein
|
In meinen Augen klar undef. Verhalten.
Da der Konstruktoraufruf noch nicht beendet wurde, hat die Lebenszeit
des Objektes a noch nicht begonnen. Da weiters auf ein non-static data
member zugegriffen wurde passt alles in 3.8/6 für UB zusammen.
Thomas |
|
| Back to top |
|
 |
Rolf Magnus Guest
|
Posted: Sun Apr 22, 2007 1:14 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
Thomas Mang wrote:
| Quote: | Marcel Müller wrote:
zei2006q1 (AT) liwest (DOT) at wrote:
Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
Die entscheidende Frage ist /wie/.
Was in 3.8/6 näher erläutert wird.
Beispiel:
f greift auf das globale Objekt a zu, bevor das globale Objekt a
fertig konstruiert worden ist:
#include <iostream
void f();
class A
{
public:
A()
{
m=5;
f();
}
void g()
{
std::cout << "m=" << m << std::endl;
}
private:
int m;
};
static A a;
void f()
{
a.g();
}
int main()
{
return 0;
}
In diesem Beispiel wird es definiertes Verhalten sein
In meinen Augen klar undef. Verhalten.
Da der Konstruktoraufruf noch nicht beendet wurde, hat die Lebenszeit
des Objektes a noch nicht begonnen.
|
Es kommt nicht darauf an, wann er beendet ist, sondern wann er anfängt (also
wann die Initialisierung der Members fertig ist). Siehe 12.7/1. |
|
| Back to top |
|
 |
Helmut Zeisel Guest
|
Posted: Mon Apr 23, 2007 12:31 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt s |
|
|
On 22 Apr., 10:14, Rolf Magnus <ramag...@t-online.de> wrote:
| Quote: | Es kommt nicht darauf an, wann er beendet ist, sondern wann er anfängt (also
wann die Initialisierung der Members fertig ist). Siehe 12.7/1.
|
Interessant. Wenn ich 12.7/1 richtig versteihe,
ist das Codebeispiel also definiertes Verhalten.
Danke,
Helmut |
|
| Back to top |
|
 |
Thomas Mang Guest
|
Posted: Mon Apr 23, 2007 5:41 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
Rolf Magnus wrote:
| Quote: | Thomas Mang wrote:
Marcel Müller wrote:
zei2006q1 (AT) liwest (DOT) at wrote:
Erlaubt der C++ Standard den Zugriff "von außen" auf ein Objekt,
dessen Konstruktor noch nicht vollständig durchlaufen worden ist?
Die entscheidende Frage ist /wie/.
Was in 3.8/6 näher erläutert wird.
Beispiel:
f greift auf das globale Objekt a zu, bevor das globale Objekt a
fertig konstruiert worden ist:
#include <iostream
void f();
class A
{
public:
A()
{
m=5;
f();
}
void g()
{
std::cout << "m=" << m << std::endl;
}
private:
int m;
};
static A a;
void f()
{
a.g();
}
int main()
{
return 0;
}
In diesem Beispiel wird es definiertes Verhalten sein
In meinen Augen klar undef. Verhalten.
Da der Konstruktoraufruf noch nicht beendet wurde, hat die Lebenszeit
des Objektes a noch nicht begonnen.
Es kommt nicht darauf an, wann er beendet ist, sondern wann er anfängt (also
wann die Initialisierung der Members fertig ist). Siehe 12.7/1.
|
Ich denke eher nicht.
Laut 3.8/1 fängt die Lebenszeit von non-PODs erst an, nachdem der
Konstruktoraufruf beendet wurde. Davor gelten Restriktionen - für Zeiger
3.8/5, für l-values 3.8/6. Ich interpretiere die Verwendung von a in f()
als l-value Verwendung gemäß 3.8/6, und da der Zugriff auf non-static
member stattfindet bevor der Konstruktoraufruf fertig ist, ist es als UB
definiert.
[Die wortwahl von 3.8/6 liest sich sehr nach Zugriff auf Objekten, die
'manuell' erzeugt und destruiert werden, also mit placement new und
direktem Destruktoraufruf. Ich denke aber nicht, dass es den hier
dargestellten Fall ausschließt - insbesondere weil die Wortwahl für
Zeiger anders, viel allgemeiner, ist].
Thomas |
|
| Back to top |
|
 |
Georg Maaß Guest
|
Posted: Tue May 01, 2007 6:18 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
Thomas Mang wrote:
| Quote: | Ich denke eher nicht.
|
Warum ist der Standard so blöd formuliert, daß hochkomptetente Leute wie
Ihr darüber streiten müssen, was gemeint sein könnte; wie sollen dann
andere, die zwar auch viel Erfahrung haben, aber weniger kompetent sind,
das dann blicken?
Da ist doch was faul und erhebliches Verbesserungspotential im Standard
vorhanden, zumindest hinsichtlich der Formulierung. |
|
| Back to top |
|
 |
Marcel Müller Guest
|
Posted: Fri May 04, 2007 11:34 pm Post subject: Re: Zugriff auf unvollständig kosntruiertes Objekt erlaubt |
|
|
Georg Maaß wrote:
| Quote: | Warum ist der Standard so blöd formuliert, daß hochkomptetente Leute wie
Ihr darüber streiten müssen, was gemeint sein könnte; wie sollen dann
andere, die zwar auch viel Erfahrung haben, aber weniger kompetent sind,
das dann blicken?
Da ist doch was faul und erhebliches Verbesserungspotential im Standard
vorhanden, zumindest hinsichtlich der Formulierung.
|
Naja, warum glauben gerade wir deutschen, dass man jede Formulierung von
Anfang an so wählen kann, dass es absolut keine Zweifelsfälle mehr gibt.
Die Sprache ist nunmal nicht 100% eindeutig, und es ist auch reichlich
viel verlangt, dass man vorher immer an absolut alle potentiellen
Fragestellungen gedacht hat. Die Realität ist eine andere. Es gibt
manchmal Zweifelsfälle und man muss den Text auch interpretieren.
Sonst wäre die ganze Gruppe hier ja überflüssig.
Marcel |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|