ATL Under the Hood Part 2
Take a look at the following Program
Program 20
#include <iostream>
using namespace std;
class Base {
public:
virtual void fun() {
cout << "Base::fun" << endl;
}
void show() {
fun();
}
};
class Drive : public Base {
public:
virtual void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive d;
d.show();
return 0;
}
The output of the program is
Drive::fun
This program clearly shows how the base class's function calls the drive class function if that function is virtual. This technique is used in different frameworks like MFC and design pattern like Template Design Pattern. Now change program little bit to see its behavior. Now I m going to call virtual function from constructor of Base class rather than member function.
Program 21
#include <iostream>
using namespace std;
class Base {
public:
Base() {
fun();
}
virtual void fun() {
cout << "Base::fun" << endl;
}
};
class Drive : public Base {
public:
virtual void fun() {
cout << "Drive::fun" << endl;
}
};
int main() {
Drive d;
return 0;
}
The output of this program is
Base::fun
This program shows that we can't call virtual function of drive class from constructor of base class. To see what is going on under the hood let's print the value of this pointer in both constructors. To make things simple remove other functions from the classes.
Program 22
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "This Pointer = " << (int*)this << endl;
cout << endl;
}
virtual void f() { cout << "Base::f" << endl; }
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "This Pointer = " << (int*)this << endl;
cout << endl;
}
virtual void f() { cout << "Drive::f" << endl; }
};
int main() {
Drive d;
cout << "In Main" << endl;
cout << (int*)&d << endl;
return 0;
}
The output of the program is
In Base This Pointer = 0012FF7C In Drive This Pointer = 0012FF7C In Main 0012FF7C
This shows that there is only one object in the memory location. Now let's print the value at this pointer, i.e. value of vptr and address of VTable.
Program 23
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = " << (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
cout << endl;
}
virtual void f1() { cout << "Base::f1" << endl; }
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
cout << endl;
}
virtual void f1() { cout << "Drive::f2" << endl; }
};
int main() {
Drive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C08C Value at Vtable = 004010F0 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C07C Value at Vtable = 00401217
This program shows the different vtable address in Base class and Drive class. To get more better understanding lets make inheritance more deep and add one more class MostDrive inherited from Drive and make an object of it.
Program 24
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
cout << endl;
}
virtual void f1() { cout << "Base::f1" << endl; }
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
cout << endl;
}
virtual void f1() { cout << "Drive::f2" << endl; }
};
class MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
cout << endl;
}
virtual void f1() {
cout << "MostDrive::f2" << endl;
}
};
int main() {
MostDrive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0A0 Value at Vtable = 004010F5 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C090 Value at Vtable = 00401221 In MostDrive Virtual Pointer = 0012FF7C Address of Vtable = 0046C080 Value at Vtable = 00401186
This program shows that virtual pointer in initialized in constructor of each class. Therefore the address of Vtable is different in each class constructor and main use the vtable of most drive class in inheritance chain whose object is created.
Now see what each class constructor place in vtable. To do this take pointer to function and store value of first entry of vtable in that function pointer and try to execute it.
Program 25
#include <iostream>
using namespace std;
typedef void(*Fun)();
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
Fun pFun = (Fun)*(int*)*(int*)this;
pFun();
cout << endl;
}
virtual void f1() { cout << "Base::f1" << endl; }
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
Fun pFun = (Fun)*(int*)*(int*)this;
pFun();
cout << endl;
}
virtual void f1() {
cout << "Drive::f1" << endl;
}
};
class MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable = "
<< (int*)*(int*)*(int*)this << endl;
Fun pFun = (Fun)*(int*)*(int*)this;
pFun();
cout << endl;
}
virtual void f1() {
cout << "MostDrive::f1" << endl;
}
};
int main() {
MostDrive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C098 Value at Vtable = 004010F5 Base::f1 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C088 Value at Vtable = 00401221 Drive::f1 In MostDrive Virtual Pointer = 0012FF7C Address of Vtable = 0046C078 Value at Vtable = 00401186 MostDrive::f1
This program shows that constructor of each class fills the vtable entries with their own virtual function. So Base class fill vtable with the address of Base virtual function and when Drive class's constructor execute it will create another vtable and store the virtual functions address.
Now see the situation when there is more than one virtual function in the base class and drive class override not all of these.
Program 26
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << "Value at Vtable 3rd entry = "
<< (int*)*((int*)*(int*)this+2) << endl;
cout << endl;
}
virtual void f1() { cout << "Base::f1" << endl; }
virtual void f2() { cout << "Base::f2" << endl; }
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << "Value at Vtable 3rd entry = "
<< (int*)*((int*)*(int*)this+2) << endl;
cout << endl;
}
virtual void f1() { cout << "Drive::f1" << endl; }
};
int main() {
Drive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0E0 Value at Vtable 1st entry = 004010F0 Value at Vtable 2nd entry = 00401145 Value at Vtable 3rd entry = 00000000 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0C8 Value at Vtable 1st entry = 0040121C Value at Vtable 2nd entry = 00401145 Value at Vtable 3rd entry = 00000000
The output of this program shows that the base class' virtual function is not overridden in drive class then drive class constructor doesn't do anything with that entry in virtual function.
Now let's invite a pure virtual function into this game and see its behavior. Take a look at the following program
Program 27
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() { cout << "Drive::f1" << endl; }
virtual void f2() { cout << "Drive::f2" << endl; }
};
int main() {
Drive d;
return 0;
}
The output of this program is little bit different in debug and release mode. Here is the output of the debug mode
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0BC Value at Vtable 1st entry = 00420CB0 Value at Vtable 2nd entry = 00420CB0 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0A4 Value at Vtable 1st entry = 00401212 Value at Vtable 2nd entry = 0040128F
And here is the output in the release Mode
In Base Virtual Pointer = 0012FF80 Address of Vtable = 0042115C Value at Vtable 1st entry = 0041245D Value at Vtable 2nd entry = 0041245D In Drive Virtual Pointer = 0012FF80 Address of Vtable = 00421154 Value at Vtable 1st entry = 00401310 Value at Vtable 2nd entry = 00401380
To better understand this, let's change the program a little bit and try to call virtual function from a function pointer.
Program 28
#include <iostream>
using namespace std;
typedef void(*Fun)();
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
// try to execute first virtual function
Fun pFun = (Fun)*((int*)*(int*)this+0);
pFun();
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() { cout << "Drive::f1" << endl; }
virtual void f2() { cout << "Drive::f2" << endl; }
};
int main() {
Drive d;
return 0;
}
Now the behavior ofthe program is different in debug and release Mode. In Debug mode it displays a run time error dialog box:

And when you press the Ignore button, one more dialog is displayed:

In release mode it just displays the error message in the output console window.
In Base Virtual Pointer = 0012FF80 Address of Vtable = 0042115C Value at Vtable 1st entry = 0041245D Value at Vtable 2nd entry = 0041245D runtime error R6025 - pure virtual function call
What is the R6025 runtime error? It is defined in CMSGS.H file which define all error messages used within the C run time library.
#define _RT_PUREVIRT_TXT "R6025" EOL \
"- pure virtual function call" EOL
In fact when you define a pure virtual function, the compiler places the address of one of the C Runtime library function _purecall. This function is define in PUREVIRT.C and have the following prototype.
void __cdecl _purecall(void)
We can achieve the same behavior by directly calling this function from a program. Let's take a look at this in a very small program.
Program 29
int main() {
_purecall();
return 0;
}
The output of this program is same as previous one in both debug and release mode. To better understand this make the inheritance chain deeper and drive one more class from Drive and see the behavior of this.
Program 30
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
};
class MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() { cout << "MostDrive::f1" << endl; }
virtual void f2() { cout << "MostDrive::f2" << endl; }
};
int main() {
MostDrive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0D8 Value at Vtable 1st entry = 00420F40 Value at Vtable 2nd entry = 00420F40 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0C0 Value at Vtable 1st entry = 00420F40 Value at Vtable 2nd entry = 00420F40 In MostDrive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0A8 Value at Vtable 1st entry = 00401186 Value at Vtable 2nd entry = 004010F5
This program shows that both Base and Drive classes make their own virtual table and initialized it with the same value. Now what happen if the inheritance is deeper and none of the class except the most drive overrides any pure virtual function? This happens in the case of COM programming where interfaces are class with only pure virtual function and one interface is inherited from another interface and only implementation class override the pure virtual function of interfaces. Then each base class constructor makes their own vtable and put the same value in its entry. So it means duplication of same code again and again.
The main philosophy of ATL is to make COM component as small as possible, but due to this behavior interface class' constructor have lot of unnecessary code. To solve this problem ATL introduce a macro ATL_NO_VTABLE define in ATLDEF.H file as
#define ATL_NO_VTABLE __declspec(novtable)
__declspec(novtable) is Microsoft C++ specific extended attribute of class. When it is used then compiler won't generate the code to initialize the vptr and vtable and reduce the generated code size.
Change our program little bit to better understand this what this attribute can do for us.
Program 31
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
};
class __declspec(novtable) MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() { cout << "MostDrive::f1" << endl; }
virtual void f2() { cout << "MostDrive::f2" << endl; }
};
int main() {
MostDrive d;
return 0;
}
The output of this program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0CC Value at Vtable 1st entry = 00420E60 Value at Vtable 2nd entry = 00420E60 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0B4 Value at Vtable 1st entry = 00420E60 Value at Vtable 2nd entry = 00420E60 In MostDrive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0B4 Value at Vtable 1st entry = 00420E60 Value at Vtable 2nd entry = 00420E60
This program shows one more result i.e Drive and MostDrive class have the same value in its vptr, but Base class have different. In fact this is due to that we haven't use __declspec(novtable) attribute with Base class. Now change program little bit and inherit Drive class with the same attributes i.e. __declspec(novtable)
Program 32
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class __declspec(novtable) Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
};
class __declspec(novtable) MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() { cout << "MostDrive::f1" << endl; }
virtual void f2() { cout << "MostDrive::f2" << endl; }
};
int main() {
MostDrive d;
return 0;
}
Now the output of the program is
In Base Virtual Pointer = 0012FF7C Address of Vtable = 0046C0C0 Value at Vtable 1st entry = 00420E50 Value at Vtable 2nd entry = 00420E50 In Drive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0C0 Value at Vtable 1st entry = 00420E50 Value at Vtable 2nd entry = 00420E50 In MostDrive Virtual Pointer = 0012FF7C Address of Vtable = 0046C0C0 Value at Vtable 1st entry = 00420E50 Value at Vtable 2nd entry = 00420E50
In MSDN it is written about __declspec(novtable) that it should be applied to pure virtual classes. Let's do one more experiment to understand meaning of this better.
Program 33
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "In Base" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0)
<< endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
virtual void f1() = 0;
virtual void f2() = 0;
};
class __declspec(novtable) Drive : public Base {
public:
Drive() {
cout << "In Drive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
}
};
class __declspec(novtable) MostDrive : public Drive {
public:
MostDrive() {
cout << "In MostDrive" << endl;
cout << "Virtual Pointer = "
<< (int*)this << endl;
cout << "Address of Vtable = "
<< (int*)*(int*)this << endl;
cout << "Value at Vtable 1st entry = "
<< (int*)*((int*)*(int*)this+0) << endl;
cout << "Value at Vtable 2nd entry = "
<< (int*)*((int*)*(int*)this+1) << endl;
cout << endl;
// try call first virtual function
typedef void (*Fun)();
Fun pFun = (Fun)*((int*)*(int*)this+0);
pFun();
}
virtual void f1() { cout << "MostDrive::f1" << endl; }
virtual void f2() { cout << "MostDrive::f2" << endl; }
};
int main() {
MostDrive d;
return 0;
}
Here the new thing we add in this program is
// try call first virtual function
typedef void (*Fun)();
Fun pFun = (Fun)*((int*)*(int*)this+0);
pFun();
And when we run the application we face the same problem as previous i.e. try to call pure virtual function. It means the virtual table haven't initialized yet. MostDrive class is not an abstract class so we should remove __declspec(novtable) from this class.
Program 34
#include <iostream>
using namespace std;
class Base {
public:
virtual void f1() = 0;
virtual void f2() = 0;
};
class __declspec(novtable) Drive : public Base {
};
class MostDrive : public Drive {
public:
MostDrive() {
// try call first virtual function
typedef void (*Fun)();
Fun pFun = (Fun)*((int*)*(int*)this+0);
pFun();
}
virtual void f1() { cout << "MostDrive::f1" << endl; }
virtual void f2() { cout << "MostDrive::f2" << endl; }
};
int main() {
MostDrive d;
return 0;
}
Now this programs work fine and output of this program is
MostDrive::f1
It is not necessary to use this attribute in ATL class only; it can be used with any class whose object can not be created. In the same way it is not necessary to must use this with ATL class, this can be omitted from ATL class, but removing this from ATL class can generate more code.
Hope to explore some other mysterious of ATL in next article.

Comments
There are no comments yet. Be the first to comment!