virtual inheritance - Default Ctor wird aufgerufen, explizit wird jedoch ein anderer aufgerufen
-
Hallo liebe Community,
also angenommen ich habe eine abstrakte Klasse namens wrapper, die wie folgt implementiert ist:
template<typename Type> class wrapper{ protected: Type* ptr; public: // constructors wrapper() : ptr(nullptr) {} wrapper(Type& type) : ptr(std::addressof(type)) {} virtual ~wrapper() = 0; }; template<typename Type> wrapper<Type>::~wrapper() {}Nun implementiere ich eine weitere abstrakte Klasse namens iterator_wrapper, die aufgrund einer Vererbungsorgie virtuell von der wrapper-Klasse erbt:
template<typename Type> class iterator_wrapper : virtual public wrapper<Type>{ public: iterator_wrapper() = default; iterator_wrapper(Type& type) : wrapper<Type>(type) {} typedef typename Type::iterator iterator; virtual ~iterator_wrapper() = 0; iterator begin() const { return wrapper<Type>::ptr->begin(); } iterator end() const { return wrapper<Type>::ptr->end(); } }; template<typename Type> iterator_wrapper<Type>::~iterator_wrapper() {}Und nun die Klasse im Einsatz. Dazu schreib ich mir eine Test-Klasse namens "Example" und teste somit ob die iterator_wrapper-Klasse funktioniert.
/*** Hier die abstrakten Klassen ***/ class Example : public iterator_wrapper<std::vector<int>>{ std::vector<int> vec; public: Example() : iterator_wrapper<decltype(vec)>(vec){ vec = {1, 2, 3, 4, 5}; } }; int main(){ for(const auto& xyz : Example()); // SEGFAULT return 0; }Das Segfault entsteht, weil aus irgend einem unbekannten Grund dennoch der Default Constructor der Klasse
wrapperaufgerufen wird. Beachtet, dass ich in den Initialisierungslisten jedoch diesen nicht aufrufe!Example::Example()ruftiterator_wrapper<Type>::iterator_wrapper(Type&)auf.
iterator_wrapper<Type>::iterator_wrapper(Type&)solltewrapper<Type>::wrapper(Type&)aufrufen.Stattdessen wird der Default Constructor der Klasse
wrapperaufgerufen, der Pointer aufnullptrinitialisiert, und wenn ich schlussendlich in der main-Funktion begin() / end() aufrufe gibts halt deswegen einen Segfault.Und imho liegt es an der virtuellen Vererbung, wobei ich da leider nicht drum herum komme.
Wenn ich statt
virtual public wrapper<Type>einfach nurpublic wrapper<Type>vererbe, gibt es keine Probleme - bis dann irgendwann bei meinen anderen wrapper-Klassen das Diamond-Problem entsteht.Meine Frage: Warum wird trotz explizitem Aufruf eines Konstruktors dennoch der Default Ctor genommen?
Ein Workaround wäre beispielsweise so:
iterator_wrapper(Type& type) : wrapper<Type>(type) /* hier wird aus unbekanntem Grund dennoch der Default Ctor aufgerufen */ { wrapper<Type>::ptr = std::addressof(type); // mit dieser Zeile Code scheint es nun endgültig zu funktionieren. }Aber dennoch würde ich gern wissen warum hier ein Default Ctor aufgerufen wird, und das Ganze auch ohne Workaround lösen.
Irgendwas muss ich doch falsch gemacht haben, oder?Kompiliert mit G++ 4.8.
Hoffe auf Hilfe.
-
Virtuelle Basisklassen werden stets direkt vom Konstruktor der am weitesten abgeleiteten Klasse (also derjeneigen, von der ein vollständiges Objekt erzeugt wird) aus initialisiert.
-
camper schrieb:
Virtuelle Basisklassen werden stets direkt vom Konstruktor der am weitesten abgeleiteten Klasse (also derjeneigen, von der ein vollständiges Objekt erzeugt wird) aus initialisiert.
Wow, Danke!

Somit:
class Example : public iterator_wrapper<std::vector<int>>{ std::vector<int> vec; public: Example() : wrapper<decltype(vec)>(vec){ vec = {1, 2, 3, 4, 5}; } };klappt es nun schlussendlich.
Cool.Was die Grundlagen angehen hab ich wohl noch ein bisschen Nachholbedarf.
-
Ich bin mir sicher, dass es einen Weg um die virtuell Vererbung rum gibt.
Ausserdem wäre ich vorsichtig, ob das was du da tust überhaupt funktioniert.vec ist zu dem Zitpunkt noch nicht konstruiert.
-
otze schrieb:
Ich bin mir sicher, dass es einen Weg um die virtuell Vererbung rum gibt.
Ja, den gibt es auch, Codeduplikation mag ich aber nicht

-
vec ist zu dem Zitpunkt noch nicht konstruiert.
Ist doch egal, oder?
Schließlich kopiere ich ihn ja nicht.
-
Pobacke schrieb:
vec ist zu dem Zitpunkt noch nicht konstruiert.
Ist doch egal, oder?
Schließlich kopiere ich ihn ja nicht.Wieso ist das wichtig? Macht doch keinen Unterschied. Etwas nicht-konstruiertes kann auch keine Adresse haben.
Pobacke schrieb:
wrapper(Type& type) : ptr(std::addressof(type)) {}
-
@out,
Bist Du Dir da auch wirklich sicher?
Ich will ja nicht das Gegenteil behaupten, aber die Ausgabe sagt da irgendwie was anderes.
-
Etwas nicht-konstruiertes kann auch keine Adresse haben.
Blödsinn.

Edit: Beziehst du dich auf den Standard? Da bin ich mir nicht sicher, da müsste man mal gucken wie das definiert ist. Vielleicht ist es wirklich UB die Adresse eines noch nicht initialisierten non-POD Objektes zu nehmen.
-
Sone schrieb:
Etwas nicht-konstruiertes kann auch keine Adresse haben.
Blödsinn.

Sonen Blödsinn Sone.
[class.cdtor]/1 schrieb:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
Sone schrieb:
Vielleicht ist es wirklich UB die Adresse eines noch nicht initialisierten non-POD Objektes zu nehmen.
Sonen Blödsinn Sone, das ist erlaubt.
-
Sone schrieb:
Etwas nicht-konstruiertes kann auch keine Adresse haben.
Blödsinn.

Ich zumindest, finde es doch sehr unlogisch, dass etwas bereits eine Adresse hat, obwohl es nocht nicht definiert wurde. Du nicht? Ich finde aber gerade auch nicht wirklich etwas dazu... Wenn aber Speicher für Member allkoiert wird, bevor der Ctor seine Arbeit antritt... komisch... man sagt doch immer Speicherreservierung geschieht bei der Definition eines Objekt

-
bullshit schrieb:
Sone schrieb:
Etwas nicht-konstruiertes kann auch keine Adresse haben.
Blödsinn.

Sonen Blödsinn Sone.
[class.cdtor]/1 schrieb:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
Es ging hier nicht darum, ob es UB ist vor der Ausführung des Konstruktors auf die Member eines Objektes zuzugreifen, sondern um die schlichte Aussage
Etwas nicht-konstruiertes kann auch keine Adresse haben.
, die natürlich sofort widerlegt ist, anhand dem Beispiel das Pobacke gebracht hat. Daher: Doch nicht sonen Blödsinn.
Etwas nicht konstruiertes muss aber schon allein deswegen eine Adresse haben, weil vor der Initialisierung eines Objektes durch den Konstruktor dafür Speicher angefordert wird. Die Adresse des ersten Bytes das von dem Objekt im Speicher belegt wird steht also klar, und das ist, weil [intro.object]/6 die Adresse eines Objektes entsprechend definiert, auch die Adresse unseres Objektes.
bullshit schrieb:
Sone schrieb:
Vielleicht ist es wirklich UB die Adresse eines noch nicht initialisierten non-POD Objektes zu nehmen.
Sonen Blödsinn Sone, das ist erlaubt.
Gut, das ist natürlich erlaubt, da hab ich mich vertan.
-
Sone schrieb:
Etwas nicht konstruiertes muss aber schon allein deswegen eine Adresse haben, weil vor der Initialisierung eines Objektes durch den Konstruktor dafür Speicher angefordert wird. Die Adresse des ersten Bytes das von dem Objekt im Speicher belegt wird steht also klar, und das ist, weil [intro.object]/6 die Adresse eines Objektes entsprechend definiert, auch die Adresse unseres Objektes.
Etwas nicht Konstruiertes muss nicht immer eine Adresse haben. Du hast aber recht, es gibt Spezialfälle in denen das so ist.
-
Sone schrieb:
Etwas nicht konstruiertes muss aber schon allein deswegen eine Adresse haben, weil vor der Initialisierung eines Objektes durch den Konstruktor dafür Speicher angefordert wird.
Hast du mir die Stelle im Draft? Ich finds net

-
Etwas nicht Konstruiertes muss nicht immer eine Adresse haben.
Damit implizierst du, dass der Konstruktor dann für das Objekt selbst Speicher anfordert.
Oder könnte es sein, dass zwar Speicher reserviert ist, aber die Adresse noch nicht feststeht?Merke: Es geht nicht darum, ob man die Adresse irgendwie im Programm bestimmen und in eine Variable stopfen kann. Es geht lediglich darum, ob die Adresse feststeht, sprich: ob man als Allwissender mit 99%iger* Wahrscheinlichkeit voraussagen kann, Objekt a wird die Adresse x haben.
out schrieb:
Sone schrieb:
Etwas nicht konstruiertes muss aber schon allein deswegen eine Adresse haben, weil vor der Initialisierung eines Objektes durch den Konstruktor dafür Speicher angefordert wird.
Hast du mir die Stelle im Draft? Ich finds net

Hmm. Warten wir bis camper kommt.
*Vielleicht gibt es ja auch einen Stromausfall, kurz bevor der Ctor aufgerufen wird...
-
Sone schrieb:
Etwas nicht Konstruiertes muss nicht immer eine Adresse haben.
Damit implizierst du, dass der Konstruktor dann für das Objekt selbst Speicher anfordert.
Oder könnte es sein, dass zwar Speicher reserviert ist, aber die Adresse noch nicht feststeht?Merke: Es geht nicht darum, ob man die Adresse irgendwie im Programm bestimmen und in eine Variable stopfen kann. Es geht lediglich darum, ob die Adresse feststeht, sprich: ob man als Allwissender mit 99%iger* Wahrscheinlichkeit voraussagen kann, Objekt a wird die Adresse x haben.
Hast du überhaupt das von mir zitierte [clas.cdtor] genauer angeschaut? Da steht genau das.
Eine etwas leichtere Kost ist das Example dazu:
[clas.cdtor] schrieb:
struct W { int j; }; struct X : public virtual W { }; struct Y { int *p; X x; Y() : p(&x.j) { // undefined, x is not yet constructed } };
-
Sone schrieb:
Etwas nicht konstruiertes muss aber schon allein deswegen eine Adresse haben, weil vor der Initialisierung eines Objektes durch den Konstruktor dafür Speicher angefordert wird.
Ok, habe nun endlich ein paar Stellen im Netz gefunden, die dich bestätigen. :p
http://yosefk.com/c++fqa/ctors.html
That's right - constructors initialize objects. In particular, constructors don't allocate the chunk of memory used for storing the object.
http://stackoverflow.com/questions/3617465/how-are-constructors-and-destructors-implemented-in-c
The constructor is called with this already pointing to the allocated memory; it need just fill in the members at that location.
Jetzt fehlt nur noch die Stelle im Draft

-
Hast du überhaupt das von mir zitierte [clas.cdtor] genauer angeschaut?
Ja. Was soll da stehen? Wann der Speicher für ein Objekt angefordert wird? Wohl kaum.
-
bullshit schrieb:
Eine etwas leichtere Kost ist das Example dazu:
struct W { int j; }; struct X : public virtual W { }; struct Y { int *p; X x; Y() : p(&x.j) { // undefined, x is not yet constructed } };Joa, das ist UB. Das haben wir nun geklärt.
Die aktuelle Frage ist gerade, wann wird der Speicher für eine Instanz angefordert und von wem.
-
...