科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件ATL布幔下的秘密之内部工作方式

ATL布幔下的秘密之内部工作方式

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

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

作者:李马编译 来源:VCKBASE 2007年10月19日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
程序9.

#include <iostream>
using namespace std;

class Class {
virtual void fun() { cout << "Class::fun" << endl; }
};

int main() {
Class objClass;

cout << "Address of virtual pointer " << (int*)(&objClass+0) << endl;
cout << "Value at virtual pointer " << (int*)*(int*)(&objClass+0) << endl;
return 0;
}

  程序的输出为:

Address of virtual pointer 0012FF7C
Value at virtual pointer 0046C060

  虚函表数指针存储了一个虚函数表的地址。并且,虚函数表存储了类中所有虚函数的地址。换句话说,虚函数表是一个数组,这个数组的元素为虚函数指针的地址。让我们看看下面这个程序来考虑吧。

  程序10.

#include <iostream>
using namespace std;

class Class {
virtual void fun() { cout << "Class::fun" << endl; }
};

typedef void (*Fun)(void);

int main() {
Class objClass;

cout << "Address of virtual pointer " << (int*)(&objClass+0) << endl;
cout << "Value at virtual pointer i.e. Address of virtual table "
<< (int*)*(int*)(&objClass+0) << endl;
cout << "Value at first entry of virtual table "
<< (int*)*(int*)*(int*)(&objClass+0) << endl;
cout << endl << "Executing virtual function" << endl << endl;
Fun pFun = (Fun)*(int*)*(int*)(&objClass+0);
pFun();
return 0;
}

  这个程序中有一些使用了类型转换的间接调用,其中最重要的一行是:

Fun pFun = (Fun)*(int*)*(int*)(&objClass+0);

  在这里,Fun为一个由typedef定义的函数指针类型:

typedef void (*Fun)(void);

  让我们来剖析一下这个冗长的间接调用。(int*)(&objClass+0)给出了虚函数表指针的地址,这个虚函数表指针是类的第一个入口,我们将它转换为了int*。要获得这个地址的值,我们需要使用间接调用运算符(也就是*),然后将它再次转换为int*,也就是(int*)*(int*)(&objClass+0)。这就会给出虚函数表的第一个入口。要获得这个位置的值,也就是获得类中第一个虚函数的地址,我们需要再次使用间接调用运算符,并将其转换为合适的函数指针类型,所以 Fun pFun = (Fun)*(int*)*(int*)(&objClass+0);
就表示从虚函数表的第一个入口获得值,作Fun的类型转换,并将其存入pFun之中。


  那么,当类中含有多于一个的虚函数,又会怎么样呢?现在我们希望存取虚函数表中的第二个虚函数,请看下面的程序:

  程序11.

#include <iostream>
using namespace std;

class Class {
virtual void f() { cout << "Class::f" << endl; }
virtual void g() { cout << "Class::g" << endl; }
};

int main() {
Class objClass;

cout << "Address of virtual pointer " << (int*)(&objClass+0) << endl;
cout << "Value at virtual pointer i.e. Address of virtual table "
<< (int*)*(int*)(&objClass+0) << endl;

cout << endl << "Information about VTable" << endl << endl;
cout << "Value at 1st entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+0) << endl;
cout << "Value at 2nd entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+1) << endl;

return 0;
}

  程序的输出为:

Address of virtual pointer 0012FF7C
Value at virtual pointer i.e. Address of virtual table 0046C0EC

Information about VTable

Value at 1st entry of VTable 0040100A
Value at 2nd entry of VTable 0040129E


  那么,有一个问题很自然地出现了——编译器是如何知道虚函数表的长度的呢?答案是:虚函数表的最后一个入口为NULL。你可以把程序改一改来考虑这个问题。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章