Hi all,
I’ve been translating a C# program to C++/CLI and I saw something that I thought It might or introduce unpredicted results to my code.
A virtual method was called from inside a base class.
That was enough motivation for me to debug the code, and actually find out that it was the virtual method of the derived class which was actually called from the constructor of the base class!
During this debugging session though, I also discovered that the constructor of the base class is called AFTER any initialization of the member variables of the derived class!
I’ve searched for some info for constructing objects in .NET, and I only found this in my first attempts..
BadBaseClass
DerivedFromBad
At first I stopped reading after I glanced at the headline, because it was clear this was the rule I knew.
Unfortunately the rest of it says that what I saw was correct behavior.
So it is reasonable, If virtual table has to be setup, to prepare derived class before base class.
But this is not valid C++ as far as I know.
The following C++/CLI program produces this call sequence
UtilityItem::UtilityItem(long)
BaseClass::BaseClass()
DerivedClass::TestVirtual()
DerivedClass::DerivedClass() //inside constructor
Instead of this call sequence.
BaseClass::TestVirtual()
Where can I find such information Because It is not easily spotted in “What's New in Visual C++ Compiler, Language, and Tools” help section
Thank you
Ioannis
ref class UtilityItem
{
public:
UtilityItem(long initU)
{
u = initU;
}
private:
long u;
};
ref class BaseClass
BaseClass() :
i(-1)
d = 1.2;
TestVirtual();
long i;
double d;
long intVal()
return i;
double dblVal()
return d;
virtual long TestVirtual()
return -234;
ref class DerivedClass : BaseClass
DerivedClass() :
ui(gcnew UtilityItem(15)),
ii(-2)
{
dd = 2.3;
TestVirtual();
}
long ii;
double dd;
UtilityItem^ ui;
long intValD()
return ii;
double dblValD()
return dd;
virtual long TestVirtual() override
int main(array<System::String ^> ^args)
Console::WriteLine(L"Hello World");
DerivedClass dd;
long i = dd.intValD();
return 0;
}
I would have also expected the BaseClass constructor to be called prior, not before as in your case, to the construction of UtilityItem in C++/CLI.
The C++ version of your code works as I would expect: the base class is constructed first.
C++/CLI does have some differences in how it resolves overloads, but I don't think this falls into that category.
If you don't get an explanation from someone in the know, you should open a bug on this.
Here's the C++/CLI standard. If this document doesn't specify the behavior of construction order, I doubt any doc will.
Brian
Followup. The initialization of an inherited class in C++/CLI should follow whatever standard that C# follows. I tried a C# variation, which instantiates members before base classes (like you're seeing in C++/CLI.) This doesn't fully resolve which of the two approaches is correct however.
namespace test2{ class X { public X() { Console.WriteLine("X ctor"); } }
class Base { public Base() { Console.WriteLine("Base ctor"); } }
class Derived : Base { public Derived() { Console.WriteLine("Derived ctor"); }
X x = new X(); }
class Program { static void Main(string[] args) { Derived d = new Derived(); } }}
This will output:
X ctorBase ctorDerived ctorwhich is roughly equivalent to your C++/CLI call order.
Yes: unlike in ISO/IEC C++ there is only one virtual function table in C++/CLI and this is initialized before any base-class constructors are called. So if you call a virtual method in a base-class constructor it will call the method in the derived class. The best place to get this information is in the ECMA C++/CLI Standard: which you can download from here:
Thank you !
Hi again,
Just for the reference of it, regarding what troubled me in the initialization sequence in the constructors, I got an answer at “Standard ECMA-335 Common Language Infrastructure (CLI)”
8.9.6.6 Constructors
...
1. Space for the new value is allocated in managed memory.
2. VES data structures of the new value are initialized and user-visible memory is zeroed.
3. The specified constructor for the object type is invoked.
Since I used an “initialization list” in my constructors, this may be assumed a “Step 2” phase so,
Constructors for member variables are valid to be called before the constructor of the base class at this point!
Jonathan, could you clarify why this CLI design must result in the calling order (as originally posted) to be different
IOW, why can't the C++/CLI and C++ both execute code in the following order:
1. set up the v-table 2. call the base class constructor (process its intializer list, and then enter its constructor)3. process the initializer list4. enter the constructor
The fact that steps 2 and 3 gets interchanged in C++/CLI seems odd, especially in light of the fact that the v-table should be there in either case.
Dear Jonathan,
I think that if the case in the constructors call sequence was as your claim it to be the result of the following program would be
-1
instead of the actual
0
Should we consider this result a C++/CLI compiler bug then
Thank you for your time
using namespace System;
ref class BaseClass abstract
long BaseInt()
ii( BaseInt() )
long ii;
long DerivedInt()
ref class SecondDerivedClass : BaseClass
SecondDerivedClass()
iii = BaseInt();
long iii;
long SecondDerivedInt()
return iii;
//Begin Of Second Test version
DerivedClass DD;
SecondDerivedClass SD;
Console::WriteLine( DD.DerivedInt() );
Console::WriteLine( SD.SecondDerivedInt() );