那么这里的R6025是什么?它定义于CMSGS.H头文件中,这个头文件定义了所有C Run Time Library的所有错误信息。
| #define _RT_PUREVIRT_TXT "R6025" EOL "- pure virtual function call" EOL |
事实上,当我们定义了纯虚函数后,编译器就会放置一个名为_purecall的C Run Time Library的函数地址。这个函数定义在 PUREVIRT.C之中,它的原型如下:
| void __cdecl _purecall(void); // 译注:原文此处无分号 |
我们可以在程序中直接调用这个函数来达到相同的效果,请看下面这个小程序:
程序29.
int main() { _purecall(); return 0; } |
这个程序在debug模式和release模式下的输出和前一个是一样的。为了更好的理解这个问题,让我们把继承链弄得更深一些,并且从Drive类中再继承一个类来看看效果吧。
程序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; } |
程序的输出为:
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 |
这个程序表明,Base和Drive类是用相同的值来初始化各自的虚函数表的。那么,如果继承更深一些,并且只有最底层的派生类重写了纯虚函数,在这种情况下又会发生什么呢?这就是在COM程序设计的情况下所发生的了——接口就是只拥有纯虚函数的类,并且一个接口是继承自另一个接口的,只有实现类才会重写接口的纯虚函数。这样一来,每个基类的构造函数就会以相同的值来初始化它们自己的虚函数表入口。所以,这就意味着相同的代码会反复重复下去。
ATL的主要思想就是让COM组件尽可能的小,但是由于这一行为,接口类的构造函数就会拥有很多不必要的代码。为了解决这一问题,ATL引入了一个宏ATL_NO_VTABLE,它定义在ATLDEF.H中:
| #define ATL_NO_VTABLE __declspec(novtable) |
__declspec(novtable)为Microsoft C++(www.cppentry.com)扩展的类属性。它会使编译器不产生初始化虚函数表指针和虚函数表的代码,这样一来就减少了生成代码的尺寸。
那么,我们来修改一下我们的代码,这样就能更好的明白这一属性究竟能为我们做什么了。
程序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; } |
程序的输出为:
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 |
这个程序有另外一个结果,也就是Drive和MostDrive类拥有相同的虚函数表地址,但是Base类却不同。事实上,这就是由于我们没有对Base类使用__declspec(novtable)属性的缘故。现在,我们来对继承的Drive类也使用相同的属性,也就是__declspec(novtable)。
|