王朝网络
分享
 
 
 

ATL布幔之下的秘密(2)

王朝other·作者佚名  2006-01-09
宽屏版  字体: |||超大  

原作者:Zeeshan Amjad

原文链接:http://www.codeproject.com/atl/atl_underthehood_2.asp

介绍

在本系列的教程中,我要讨论一些ATL的内部工作方式以及它所使用的技术,这是本系列的第二篇文章。

现在让我们来探究一些虚函数背后更加有趣的资料。为了和上文保持一致,在本文的讨论中我将使用相同的顺序,程序的序号从20开始。

让我们看看下面这个程序:

程序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;

}

程序的输出为:

Drive::fun

这个程序清楚地示范了基类的函数是如何调用派生类的虚函数的。这一技术被用于不同的框架中,例如MFC和设计模式(比如Template Design Pattern)。现在你可以修改一下这个程序来看看它的行为,我将要在基类的构造函数中调用虚函数,而不是普通的成员函数。

程序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;

}

程序的输出为:

Base::fun

这个程序表明,我们不能在基类的构造函数中调用派生类的虚函数。好了,那就让我们来看看着布幔之下到底做了什么。我将会把这些构造函数之中的指针值打印出来,为了简便起见,我移除了类中其它的函数。

程序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;

}

程序的输出为:

In Base

This Pointer = 0012FF7C

In Drive

This Pointer = 0012FF7C

In Main

0012FF7C

这就表示,整个内存位置中,只有一个对象的存在。那么就让我们把这个指针指向的值打印出来,也就是虚函数表的指针vptr指向的值,VTable的地址。

程序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;

}

程序的输出为:

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

这个程序示范了基类和派生类中不同的虚函数表地址。为了更好地弄懂这一问题,那就让我们把继承层次加深,并添加一个继承于Drive类的MostDrive类,然后构建一个它的对象。

程序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;

}

程序的输出为:

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

这个程序示范了虚函数表指针在每个类的构造函数中的初始化过程。这样看来,每个类构造函数中虚函数表的地址是不同的,main函数则使用了继承链中的最底部的派生类来创建对象。

现在你可以看看虚函数表中各个类的构造函数的位置了,你可以将虚函数表中的第一个入口存入一个函数指针,并尝试运行它。

程序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;

}

程序的输出为:

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

这个程序示范了每个类中的构造函数是如何用自己的虚函数来填充虚函数表中的各入口的。所以,Base类使用Base类的虚函数地址来填充自己的虚函数表,当Drive类的构造函数执行它的时候会创建另外一个虚函数表,并存储自己的虚函数地址。

现在,你会发现在基类中含有多个虚函数的情况下,派生类并不能完全重写它们。

程序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;

}

程序的输出为:

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

这个程序的输出表明基类的虚函数在派生类中并未被重写,然后,派生类的构造函数没有对虚函数的入口做任何的事情。

那么现在,让我邀请纯虚函数来加入这一游戏,再来看看它的行为吧。请看以下的程序:

程序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;

}

在debug和release模式下,程序的输出有所不同。下面是debug模式的输出:

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

以下则是release模式的输出:

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

为了更好地弄懂这一原理,我们需要对程序作少许的改动,并尝试使用函数指针来调用虚函数。

程序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;

// 尝试执行第一个虚函数

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;

}

现在程序的行为在debug和release模式下仍然不同。在debug模式下,它会显示一个运行时错误的对话框:

并且,当你按下“忽略”按钮之后,它会显示下面的对话框:

而在release模式下运行的话,它只会在控制台窗口中输出错误信息:

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

那么这里的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++扩展的类属性。它会使编译器不产生初始化虚函数表指针和虚函数表的代码,这样一来就减少了生成代码的尺寸。

那么,我们来修改一下我们的代码,这样就能更好的明白这一属性究竟能为我们做什么了。

程序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)。

程序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;

}

现在,程序的输出为:

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

在MSDN中,对__declspec(novtable)的解释是:它应该使用在纯虚类中。那么,让我们再做一个实验来更好地弄懂这一含义吧。

程序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;

// 尝试调用第一个虚函数

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;

}

我们在程序中添加的新东西是:

// 尝试调用第一个虚函数

typedef void (*Fun)();

Fun pFun = (Fun)*((int*)*(int*)this+0);

pFun();

并且,当我们运行这个应用程序的时候,我们会面对与前一个程序相同的问题——也就是尝试调用虚函数发生的错误。这就意味着虚函数表并未初始化。MostDrive类并不是一个抽象类,所以我们应该从类中移除__declspec(novtable)。

程序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() {

// 尝试调用第一个虚函数

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;

}

现在,程序就可以正常工作了。它的输出为:

MostDrive::f1

这个属性并不一定只用在ATL的类中,它可以对任何不能创建对象的类使用。同样,它也并不一定非要用在ATL类中,也就是说它可以从ATL类中省略,不过这就意味着这样会产生更多的代码。

我希望能在下篇文章中探究更多ATL的秘密。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有