您的位置:首页 > 其它

ATL技术内幕 第二部分

2012-06-09 13:12 656 查看
题目:ATL Under the Hood - Part 2

原文链接:http://www.codeproject.com/Articles/1846/ATL-Under-the-Hood-Part-2

介绍
这个系列我们将讨论ATL的内部工作机制以及技术实现。这是第二部分。
现在,让我们揭开虚函数背后的那些有趣的东西。(为了与第一部分保持连续性,我延续前面的程序数)
下面我们看这段代码:

程序
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 和 像模板设计模式之类的设计模式等。现在,我们对程序稍作改动,来看看其行为。现在,我们在基类的构造函数而不是成员函数中调用虚函数。

程序
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
这个程序显示,我们不可以在基类的构造函数中调用派生类的虚函数。为了看清楚内部原因,我们在两个构造函数中分别打印this指针(该类自身的指针)。为了简化,我们移除了类中其他的函数。

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

结果表明:在内存中只有一个对象。现在我们来打印this指针的值,也就是虚指针的值,以及虚表的值(也就是虚指针值所指的对象的值)。

程序
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
这个程序显示基类和派生类有着不同的虚表地址。为了更好地理解,让我们再添加一层的继承,然后构造一个该类的对象。

程序
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
这个程序显示虚指针是在每个类的构造函数中初始化的。因此,虚表的地址在每个类的构造函数中是不同的,而主函数创建了继承链最下层的对象,并调用这个对象的虚表。
现在我们看看每个类的构造函数在虚表中放了什么。为此,我们要利用函数指针来存储虚表中的第一个元素,然后来执行它。

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

这个程序显示每个类的构造函数利用自己的虚函数填充虚表。也就是说,基类用基类的虚函数地址填充虚表,当派生类构造函数执行时,他将创建另一个虚表然后将其自身的虚函数地址存入。

现在我们看看这种情况:基类有多个虚函数,而子类重写了其中的一部分。

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

// 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;
}

现在,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运行时库的错误消息。

#define _RT_PUREVIRT_TXT   "R6025" EOL "- pure virtual function call" EOL

实际上,当我们定义一个纯虚函数时,编译器会将C运行时库的函数之一:_purecall放入虚表中。这个函数在PUREVIRT.C文件中定义,有如下的原型:

void __cdecl _purecall(void)

我们可以通过直接调用这个函数得到上述同样的结果。让我们看看这个非常袖珍的程序


程序
29

int main() {
_purecall();
return 0;
}

这个程序的输出不管在debug合适release模式下和前面的那个都完全一样。为了更好地理解其中的缘由,我们将继承链加深,再加一层继承,看看结果如何:

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

这个程序结果显示,父类和子类都构造他们自己的虚表,并且用同样的值来初始化。如果继承链更加长,而且除了最底层的派生类外,其他派生类都没有重写任何的父类纯虚函数,这时会发生什么情况呢?这种情况其实在COM编程时就会遇到,因为COM编程中,接口就是只有纯虚函数的类,一个接口继承自另一个接口,他们都没有实现这些接口中的纯虚函数,直到实现类重写了这些接口中的纯虚函数。此时,每一个父类构造函数构造他们自己的虚表,并且放同样的值到其虚表入口,这意味着同样的代码在进行着大量的重复。

ATL主要的理念就是让COM组件尽可能的小,但是由于虚表的存在,接口类的构造函数含有大量不必要的代码。为解决这个问题,ATL引入一个宏
ATL_NO_VTABLE
,定义在
ATLDEF.H
中:


#define ATL_NO_VTABLE __declspec(novtable)

__declspec(novtable) 是微软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类不一样。这是因为我们没有在基类中利用
__declspec(novtable)
属性。现在稍作改变,我们用同样的属性修饰Drive类

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

// 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;
}

现在,这个程序运行很好,输出结果如下:
MostDrive::f1

这个属性不一定非得用于ATL类,也可以用在任何不允许实例化的抽象类。当然,ATL也不是非要用这个属性不可,只是移除了这个属性将使得ATL类产生更多的代码量。

本部分完。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息