Angående argumenten, inser att det finns ett ännu enklare sätt att separera allokering och ctor som även gör så att argumenten trillar på plats helt automatiskt.
allokera minne endera på stacken, heapen eller custon new (som måste returnera pekare till utrymmet enligt C++ språkstandard), lägg adressen till minnet där första argumentet ska vara (this)
stoppa dit alla argument som ctor tar
anropa ctor, this pekar nu på objektutrymmet och resten av argument är där de förväntas vara
I detta läge kan man ha samma minnesallokeringslogik för alla typer oavsett antal argument till ctor och ctor behöver överhuvudtaget inte bry sig om hur minnet allokeras.
Beskriver du inte bara den vanliga ooptimerade modellen här, eller har jag missat något?
Beskrivning du gör är typ omvänd Pascal-calling och det blir problem vid automatiska variabler då det är anropad funktion som allokerar utrymmet på stacken -> helt omöjligt att hoppa tillbaka till den som anropar på "normalt" sätt då returadress normalt ligger på stacken och den typen av instruktioner modifierar stackpekare (vars topp nu är utrymmet för this!). Även om man kommer tillbaka har nu den som anropar ett problem, den måste veta hur mycket minne ctor allokerade annars går det inte att fria minnet på korrekt sätt -> extra tillstånd som tar instruktioner och utrymme någonstans.
Jag kan inte alla detaljer. Jag har bara studerat den genererade koden.
Autoobjekten har fått sitt minne reserverat på stacken i caller och den adressen kommer in i this-pekaren. De behöver inte allokera minne i den optimerade konstruktorn. Det minne som reserverades på stacken kommer, precis som för alla andra auto-objekt på stacken, släppas när caller returnerar.
Det är bara för de dynamiskt allokerade objekten som konstruktorn allokerar minne. För dessa objekt kan kompilatorn ersätta anropen till operator new och det efterföljande konstruktoranropet till en kombinerad allokator+konstruktor funktion som både allokerar minne och konstruerar objektet. Signaleringen att minne behöver allokeras kan skötas på olika sätt, det enklaste är nog att this-pekaren är NULL, men man kan även ha en separat flagga. Denna kombinerade funktion kan även användas för konstruktion av auto-objekt. Då skickar man in adressen där objektet skall konstrueras och då anropar den kombinerade funktionen inte operator new utan konstruerar objektet i det minne som this-pekaren pekar ut. En och samma funktion kan anropas för villkorlig allokering och konstruktion objekt, oavsett om det är ett auto-objekt eller dynamiskt
Motsvarande trick går att göra destruktorsidan, men det kan man nog bara göra om destruktorn är virtuell. Annars vet man nog inte om det är en klasspecifik operator delete eller den globala som skall användas för släppa minnet. Återigen är det bara de dynamiskt allokerade objektens destruktorer som skall frigöra minnet och här behövs det nog en extra parameter som talar om om det skall ske.
Dessa optimeringar kan naturligtvis inte göras om man följer ett ABI som specar att det skall gå till på ett annat sätt.
Nu ger jag upp. Om detta inte räcker för att du skall acceptera att det går att göra kan vi avsluta den här diskussionen nu. Vi har kommit ganska långt OT...
Om du fortfarande inte tror att kompilatorerna kan skicka extra argument eller göra saker bakom ryggen på dig kan du fundera på olika sätt att lösa problemet med att konstruktorna för B och C måste veta om de skall anropa sin virtuella bas eller inte. Se exemplet nedan. Vilken av de olika lösningarna du kommer på är mest effektiv ur storlekssynpunkt.
class V { V(); } ;
class B : virtual public V { B(); };
class C : virtual public V { C(); };
class D : public B, public C { D() : V(), B(), C() {} }
void fun()
{
new C; // Här skall C::C() anropa V::V()
new D; // Här skall B::b::() och C::C() inte anropa V::V()
}