- 浏览: 1161575 次
文章分类
最新评论
-
dukunpeng22:
想问楼主,关于实时通讯,Socket是最方便的吗,这个东西在客 ...
android 聊天室 -
greatwqs:
想问一下楼主的HBase版本是多少呢?
我这边用的是hbas ...
HTablePool的实现分析 -
fengpengfei8910:
兄弟 不知道你做出来没
通过WebView实现页面与ANDRIOD客户端的交互 -
guji528:
正在找Python调试程序,改天试试FWTools了
Eclipse中调试Python代码--调试FWTools2.4.7中的gdal_retile.py -
guji528:
要增加对 Python 交互式执行的支持,可通过Run > ...
PyDev
ATL布幔之下的秘密(2)
介绍
在本系列的教程中,我要讨论一些ATL的内部工作方式以及它所使用的技术,这是本系列的第二篇文章。
现在让我们来探究一些虚函数背后更加有趣的资料。为了和上文保持一致,在本文的讨论中我将使用相同的顺序,程序的序号从20开始。
让我们看看下面这个程序:
程序20.
#include<iostream>
usingnamespacestd;
classBase{
public:
virtualvoidfun(){
cout<<"Base::fun"<<endl;
}
voidshow(){
fun();
}
};
classDrive:publicBase{
public:
virtualvoidfun(){
cout<<"Drive::fun"<<endl;
}
};
intmain(){
Drived;
d.show();
return0;
}
程序的输出为:
Drive::fun
这个程序清楚地示范了基类的函数是如何调用派生类的虚函数的。这一技术被用于不同的框架中,例如MFC和设计模式(比如TemplateDesignPattern)。现在你可以修改一下这个程序来看看它的行为,我将要在基类的构造函数中调用虚函数,而不是普通的成员函数。
程序21.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
fun();
}
virtualvoidfun(){
cout<<"Base::fun"<<endl;
}
};
classDrive:publicBase{
public:
virtualvoidfun(){
cout<<"Drive::fun"<<endl;
}
};
intmain(){
Drived;
return0;
}
程序的输出为:
Base::fun
这个程序表明,我们不能在基类的构造函数中调用派生类的虚函数。好了,那就让我们来看看着布幔之下到底做了什么。我将会把这些构造函数之中的指针值打印出来,为了简便起见,我移除了类中其它的函数。
程序22.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"ThisPointer="<<(int*)this<<endl;
cout<<endl;
}
virtualvoidf(){cout<<"Base::f"<<endl;}
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"ThisPointer="<<(int*)this<<endl;
cout<<endl;
}
virtualvoidf(){cout<<"Drive::f"<<endl;}
};
intmain(){
Drived;
cout<<"InMain"<<endl;
cout<<(int*)&d<<endl;
return0;
}
程序的输出为:
InBase
ThisPointer=0012FF7C
InDrive
ThisPointer=0012FF7C
InMain
0012FF7C
这就表示,整个内存位置中,只有一个对象的存在。那么就让我们把这个指针指向的值打印出来,也就是虚函数表的指针vptr指向的值,VTable的地址。
程序23.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Base::f1"<<endl;}
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f2"<<endl;}
};
intmain(){
Drived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C08C
ValueatVtable=004010F0
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C07C
ValueatVtable=00401217
这个程序示范了基类和派生类中不同的虚函数表地址。为了更好地弄懂这一问题,那就让我们把继承层次加深,并添加一个继承于Drive类的MostDrive类,然后构建一个它的对象。
程序24.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Base::f1"<<endl;}
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f2"<<endl;}
};
classMostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0A0
ValueatVtable=004010F5
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C090
ValueatVtable=00401221
InMostDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C080
ValueatVtable=00401186
这个程序示范了虚函数表指针在每个类的构造函数中的初始化过程。这样看来,每个类构造函数中虚函数表的地址是不同的,main函数则使用了继承链中的最底部的派生类来创建对象。
现在你可以看看虚函数表中各个类的构造函数的位置了,你可以将虚函数表中的第一个入口存入一个函数指针,并尝试运行它。
程序25.
#include<iostream>
usingnamespacestd;
typedefvoid(*Fun)();
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
FunpFun=(Fun)*(int*)*(int*)this;
pFun();
cout<<endl;
}
virtualvoidf1(){cout<<"Base::f1"<<endl;}
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
FunpFun=(Fun)*(int*)*(int*)this;
pFun();
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f1"<<endl;}
};
classMostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable="<<(int*)*(int*)*(int*)this<<endl;
FunpFun=(Fun)*(int*)*(int*)this;
pFun();
cout<<endl;
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
};
intmain(){
MostDrived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C098
ValueatVtable=004010F5
Base::f1
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C088
ValueatVtable=00401221
Drive::f1
InMostDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C078
ValueatVtable=00401186
MostDrive::f1
这个程序示范了每个类中的构造函数是如何用自己的虚函数来填充虚函数表中的各入口的。所以,Base类使用Base类的虚函数地址来填充自己的虚函数表,当Drive类的构造函数执行它的时候会创建另外一个虚函数表,并存储自己的虚函数地址。
现在,你会发现在基类中含有多个虚函数的情况下,派生类并不能完全重写它们。
程序26.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<"ValueatVtable3rdentry="<<(int*)*((int*)*(int*)this+2)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Base::f1"<<endl;}
virtualvoidf2(){cout<<"Base::f2"<<endl;}
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<"ValueatVtable3rdentry="<<(int*)*((int*)*(int*)this+2)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f1"<<endl;}
};
intmain(){
Drived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0E0
ValueatVtable1stentry=004010F0
ValueatVtable2ndentry=00401145
ValueatVtable3rdentry=00000000
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0C8
ValueatVtable1stentry=0040121C
ValueatVtable2ndentry=00401145
ValueatVtable3rdentry=00000000
这个程序的输出表明基类的虚函数在派生类中并未被重写,然后,派生类的构造函数没有对虚函数的入口做任何的事情。
那么现在,让我邀请纯虚函数来加入这一游戏,再来看看它的行为吧。请看以下的程序:
程序27.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f1"<<endl;}
virtualvoidf2(){cout<<"Drive::f2"<<endl;}
};
intmain(){
Drived;
return0;
}
在debug和release模式下,程序的输出有所不同。下面是debug模式的输出:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0BC
ValueatVtable1stentry=00420CB0
ValueatVtable2ndentry=00420CB0
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0A4
ValueatVtable1stentry=00401212
ValueatVtable2ndentry=0040128F
以下则是release模式的输出:
InBase
VirtualPointer=0012FF80
AddressofVtable=0042115C
ValueatVtable1stentry=0041245D
ValueatVtable2ndentry=0041245D
InDrive
VirtualPointer=0012FF80
AddressofVtable=00421154
ValueatVtable1stentry=00401310
ValueatVtable2ndentry=00401380
为了更好地弄懂这一原理,我们需要对程序作少许的改动,并尝试使用函数指针来调用虚函数。
程序28.
#include<iostream>
usingnamespacestd;
typedefvoid(*Fun)();
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
//尝试执行第一个虚函数
FunpFun=(Fun)*((int*)*(int*)this+0);
pFun();
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"Drive::f1"<<endl;}
virtualvoidf2(){cout<<"Drive::f2"<<endl;}
};
intmain(){
Drived;
return0;
}
现在程序的行为在debug和release模式下仍然不同。在debug模式下,它会显示一个运行时错误的对话框:
并且,当你按下“忽略”按钮之后,它会显示下面的对话框:
而在release模式下运行的话,它只会在控制台窗口中输出错误信息:
InBase
VirtualPointer=0012FF80
AddressofVtable=0042115C
ValueatVtable1stentry=0041245D
ValueatVtable2ndentry=0041245D
runtimeerrorR6025
-purevirtualfunctioncall
那么这里的R6025是什么?它定义于CMSGS.H头文件中,这个头文件定义了所有CRunTimeLibrary的所有错误信息。
#define_RT_PUREVIRT_TXT"R6025"EOL"-purevirtualfunctioncall"EOL
事实上,当我们定义了纯虚函数后,编译器就会放置一个名为_purecall的CRunTimeLibrary的函数地址。这个函数定义在PUREVIRT.C之中,它的原型如下:
void__cdecl_purecall(void);//译注:原文此处无分号
我们可以在程序中直接调用这个函数来达到相同的效果,请看下面这个小程序:
程序29.
intmain(){
_purecall();
return0;
}
这个程序在debug模式和release模式下的输出和前一个是一样的。为了更好的理解这个问题,让我们把继承链弄得更深一些,并且从Drive类中再继承一个类来看看效果吧。
程序30.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
};
classMostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
virtualvoidf2(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0D8
ValueatVtable1stentry=00420F40
ValueatVtable2ndentry=00420F40
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0C0
ValueatVtable1stentry=00420F40
ValueatVtable2ndentry=00420F40
InMostDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0A8
ValueatVtable1stentry=00401186
ValueatVtable2ndentry=004010F5
这个程序表明,Base和Drive类是用相同的值来初始化各自的虚函数表的。那么,如果继承更深一些,并且只有最底层的派生类重写了纯虚函数,在这种情况下又会发生什么呢?这就是在COM程序设计的情况下所发生的了——接口就是只拥有纯虚函数的类,并且一个接口是继承自另一个接口的,只有实现类才会重写接口的纯虚函数。这样一来,每个基类的构造函数就会以相同的值来初始化它们自己的虚函数表入口。所以,这就意味着相同的代码会反复重复下去。
ATL的主要思想就是让COM组件尽可能的小,但是由于这一行为,接口类的构造函数就会拥有很多不必要的代码。为了解决这一问题,ATL引入了一个宏ATL_NO_VTABLE,它定义在ATLDEF.H中:
#defineATL_NO_VTABLE__declspec(novtable)
__declspec(novtable)为MicrosoftC++扩展的类属性。它会使编译器不产生初始化虚函数表指针和虚函数表的代码,这样一来就减少了生成代码的尺寸。
那么,我们来修改一下我们的代码,这样就能更好的明白这一属性究竟能为我们做什么了。
程序31.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
classDrive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
};
class__declspec(novtable)MostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
virtualvoidf2(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0CC
ValueatVtable1stentry=00420E60
ValueatVtable2ndentry=00420E60
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0B4
ValueatVtable1stentry=00420E60
ValueatVtable2ndentry=00420E60
InMostDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0B4
ValueatVtable1stentry=00420E60
ValueatVtable2ndentry=00420E60
这个程序有另外一个结果,也就是Drive和MostDrive类拥有相同的虚函数表地址,但是Base类却不同。事实上,这就是由于我们没有对Base类使用__declspec(novtable)属性的缘故。现在,我们来对继承的Drive类也使用相同的属性,也就是__declspec(novtable)。
程序32.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
class__declspec(novtable)Drive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
};
class__declspec(novtable)MostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
virtualvoidf2(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
现在,程序的输出为:
InBase
VirtualPointer=0012FF7C
AddressofVtable=0046C0C0
ValueatVtable1stentry=00420E50
ValueatVtable2ndentry=00420E50
InDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0C0
ValueatVtable1stentry=00420E50
ValueatVtable2ndentry=00420E50
InMostDrive
VirtualPointer=0012FF7C
AddressofVtable=0046C0C0
ValueatVtable1stentry=00420E50
ValueatVtable2ndentry=00420E50
在MSDN中,对__declspec(novtable)的解释是:它应该使用在纯虚类中。那么,让我们再做一个实验来更好地弄懂这一含义吧。
程序33.
#include<iostream>
usingnamespacestd;
classBase{
public:
Base(){
cout<<"InBase"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
virtualvoidf1()=0;
virtualvoidf2()=0;
};
class__declspec(novtable)Drive:publicBase{
public:
Drive(){
cout<<"InDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
}
};
class__declspec(novtable)MostDrive:publicDrive{
public:
MostDrive(){
cout<<"InMostDrive"<<endl;
cout<<"VirtualPointer="<<(int*)this<<endl;
cout<<"AddressofVtable="<<(int*)*(int*)this<<endl;
cout<<"ValueatVtable1stentry="<<(int*)*((int*)*(int*)this+0)<<endl;
cout<<"ValueatVtable2ndentry="<<(int*)*((int*)*(int*)this+1)<<endl;
cout<<endl;
//尝试调用第一个虚函数
typedefvoid(*Fun)();
FunpFun=(Fun)*((int*)*(int*)this+0);
pFun();
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
virtualvoidf2(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
我们在程序中添加的新东西是:
//尝试调用第一个虚函数
typedefvoid(*Fun)();
FunpFun=(Fun)*((int*)*(int*)this+0);
pFun();
并且,当我们运行这个应用程序的时候,我们会面对与前一个程序相同的问题——也就是尝试调用虚函数发生的错误。这就意味着虚函数表并未初始化。MostDrive类并不是一个抽象类,所以我们应该从类中移除__declspec(novtable)。
程序34.
#include<iostream>
usingnamespacestd;
classBase{
public:
virtualvoidf1()=0;
virtualvoidf2()=0;
};
class__declspec(novtable)Drive:publicBase{
};
classMostDrive:publicDrive{
public:
MostDrive(){
//尝试调用第一个虚函数
typedefvoid(*Fun)();
FunpFun=(Fun)*((int*)*(int*)this+0);
pFun();
}
virtualvoidf1(){cout<<"MostDrive::f1"<<endl;}
virtualvoidf2(){cout<<"MostDrive::f2"<<endl;}
};
intmain(){
MostDrived;
return0;
}
现在,程序就可以正常工作了。它的输出为:
MostDrive::f1
这个属性并不一定只用在ATL的类中,它可以对任何不能创建对象的类使用。同样,它也并不一定非要用在ATL类中,也就是说它可以从ATL类中省略,不过这就意味着这样会产生更多的代码。
我希望能在下篇文章中探究更多ATL的秘密。
相关推荐
适合人群: 对森林、地形、冰川等地物高度研究的人群 如何将星载lidar ICESAT-2的atl8和atl3结合,综合利用地理定位信息与地面高度信息
ATL的发明人Jim Springfield亲自作序推荐 四位顶尖的Windows编程专家倾力合作,绝对经典再现 COM、ATL开发人员的必备宝典 深入分析ATL实现COM内幕细节,展示COM应用中的各类漂亮技巧 本书主要介绍了ATL技术的...
atl开发指南 atl开发指南 atl开发指南 atl开发指南 atl开发指南
ATL之深入浅出,描述ATL结构,各种宏定义的展开等
python实现ICESat-2 ATL08转shapefile
ATL技术内幕ATL技术内幕ATL技术ATL技术内幕内幕
中文名: 深入解析ATL(第2版) 原名: ATL Internals, 2nd Edition Working with ATL 8 别名: ATL 作者: (美)塔瓦瑞斯译者: 赖仪灵 曹雨田 资源格式: PDF 版本: 扫描版 出版社: 电子工业出版社书号: 9787121049859...
中文版 清晰 pdf,经典ATL又一升级力作,...因为不能大于60M,所以只能分开为2部分了。 原书名: ATL Internals: Working with ATL 8 (2nd Edition) 作者: (美)Chris Tavares Kirk Fertitta Brent Rector Chris Sells
深入解析ATL(第2版)PDF 高清中文版 ATL8.0 VS2005 part5
当我们用VC++ ATL 工程创建了一个COM 工程,实现了一个自己的COM 对象,又在另一个程序中CoCreateInstance 这个COM 对象时,不知你是否想过这样的问题:COM 对象是用C++类对象实现的,但是,我们从来没有在自己的...
VS2003下编译通过,包含两个ATL的例子,1)创建一个简单ATL对象,目的弹出一个Messagebox输出一句话,附加测试程序。程序中要注意COM的初始化。 2)创建一个ATL控件,嵌入到网页中,实现功能为,点击控件中三角形...
ATL开发指南.rar 完整的 ATL开发指南 提供下载 学习com atl编程非常好的资料
ATL+WTL,Windows平台仍然是一对锋利的组合。 主要特点: http://blogs.msdn.com/b/vcblog/archive/2013/08/20/atl-and-mfc-changes-and-fixes-in-visual-studio-2013.aspx One of the major changes we made was ...
ATL简明教程 1. 介绍ATL的使用.
PhoREAL_v3.26安装,可以批量处理ICESat2的ATL03和ATL08数据
ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏ATL字符串转换宏
深入解析ATL 第2版
ATL接口映射宏详解 ATL接口映射宏详解 ATL接口映射宏详解
PhoREAL_v3.26安装包,可以批量处理ICESat2的ATL03和ATL08数据
collection.zip:包含VC Atl开发的集合的源代码(组件程序和测试程序) enum.zip:包含VC Atl开发的枚举器的源代码(组件程序和测试程序) event.zip:包含VC Atl开发的事件的源代码(组件程序和测试程序) win....