首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
ASP | ASP.NET | JSP | PHP | AJAX | XML | Java script | HTML/CSS | 服务器类
各大城市软件开发培训、软件人才免费咨询热线:400-700-5807
 您现在的位置: 中国IT实验室 >> WEB开发 >> WEB开发 >> 正文
ATL布幔下的秘密之内部工作方式(2)
ChinaItLab  2004-12-29  保存本文    收藏本站


  程序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。你可以把程序改一改来考虑这个问题。
  
  程序12.
  #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;
  cout << "Value at 3rd entry of VTable "
  << (int*)*((int*)*(int*)(&objClass+0)+2) << endl;
  cout << "Value at 4th entry of VTable "
  << (int*)*((int*)*(int*)(&objClass+0)+3) << endl;
  
  return 0;
  }
  
  程序的输出为:
  
  Address of virtual pointer 0012FF7C
  Value at virtual pointer i.e. Address of virtual table 0046C134
  
  Information about VTable
  
  Value at 1st entry of VTable 0040100A
  Value at 2nd entry of VTable 0040129E
  Value at 3rd entry of VTable 00000000
  Value at 4th entry of VTable 73616C43
  
  这个程序的输出示范了虚函数表的最后一个入口为NULL。就让我们来用已有的知识来调用虚函数吧:
   
  程序13.
  #include <iostream>
  using namespace std;
  
  class Class {
  virtual void f() { cout << "Class::f" << endl; }
  virtual void g() { cout << "Class::g" << endl; }
  };
  
  typedef void(*Fun)(void);
  
  int main() {
  Class objClass;
  
  Fun pFun = NULL;
  
  // 调用第一个虚函数
  pFun = (Fun)*((int*)*(int*)(&objClass+0)+0);
  pFun();
  
  // 调用第二个虚函数
  pFun = (Fun)*((int*)*(int*)(&objClass+0)+1);
  pFun();
  
  return 0;
  }
  
  程序的输出为:
  
  Class::f
  Class::g
  
  现在我们来看看多重继承的情况。先看一个多重继承最简单的情况:
  
  程序14.
  #include <iostream>
  using namespace std;
  
  class Base1 {
  public:
  virtual void f() { }
  };
  
  class Base2 {
  public:
  virtual void f() { }
  };
  
  class Base3 {
  public:
  virtual void f() { }
  };
  
  class Drive : public Base1, public Base2, public Base3 {
  };
  
  int main() {
  Drive objDrive;
  cout << "Size is = " << sizeof(objDrive) << endl;
  return 0;
  }
  
  程序的输出为:Size is = 12
  
  这个程序示范了当你从多个基类继承一个类的时候,这个派生类就会拥有所有基类的虚函数表指针。
   
  那么,当派生类也有虚函数表指针的时候会发生什么事情呢?让我们看看下面的程序来弄懂关于带有虚函数的多重继承的概念吧。
  
  程序15.
  #include <iostream>
  using namespace std;
  
  class Base1 {
  virtual void f() { cout << "Base1::f" << endl; }
  virtual void g() { cout << "Base1::g" << endl; }
  };
  
  class Base2 {
  virtual void f() { cout << "Base2::f" << endl; }
  virtual void g() { cout << "Base2::g" << endl; }
  };
  
  class Base3 {
  virtual void f() { cout << "Base3::f" << endl; }
  virtual void g() { cout << "Base3::g" << endl; }
  };
  
  class Drive : public Base1, public Base2, public Base3 {
  public:
  virtual void fd() { cout << "Drive::fd" << endl; }
  virtual void gd() { cout << "Drive::gd" << endl; }
  };
  
  typedef void(*Fun)(void);
  
  int main() {
  Drive objDrive;
  
  Fun pFun = NULL;
  
  // 调用Base1的第一个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+0);
  pFun();
  
  // 调用Base1的第二个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+1);
  pFun();
  
  // 调用Base2的第一个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+0);
  pFun();
  
  // 调用Base2的第二个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+1);
  pFun();
  
  // 调用Base3的第一个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+0);
  pFun();
  
  // 调用Base3的第二个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+1);
  pFun();
  
  // 调用派生类的第一个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+2);
  pFun();
  
  // 调用派生类的第二个虚函数
  pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+3);
  pFun();
  
  return 0;
  }
  
  程序的输出为:
  
  Base1::f
  Base1::g
  Base2::f
  Base2::f
  Base3::f
  Base3::f
  Drive::fd
  Drive::gd
   
  我们可以通过使用static_cast获得派生类虚函数表指针的偏移量,请看以下程序:
  
  程序16.
  #include <iostream>
  using namespace std;
  
  class Base1 {
  public:
  virtual void f() { }
  };
  
  class Base2 {
  public:
  virtual void f() { }
  };
  
  class Base3 {
  public:
  virtual void f() { }
  };
  
  class Drive : public Base1, public Base2, public Base3 {
  };
  
  // 任意的非0值,因为0乘任何数都得0
  #define SOME_VALUE 1
  
  int main() {
  cout << (DWORD)static_cast<Base1*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
  cout << (DWORD)static_cast<Base2*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
  cout << (DWORD)static_cast<Base3*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
  return 0;
  }
  
  ATL使用了一个定义在ATLDEF.H中的宏offsetofclass来做这件事,这个宏被定义为:
  
  #define offsetofclass(base, derived) ((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)
  
  这个宏返回了在派生类对象模型中基类虚函数表指针的偏移量,让我们来看看下面这个例子:
  
  程序17.
  #include <windows.h>
  #include <iostream>
  using namespace std;
  
  
中国IT教育热线咨询
相关文章
Flash+PHP+Mysql简单留言本制作实例教程
基础知识:Java Web三层架构的配置详解
使用AJAX技术构建更优秀的Web应用程序
ASP应用程序设计的Web状态管理分析
Ajax驱动的Web站点
最新文章
·Ajax光环背后的隐患
·Windows操作系统下JSP程序开发环
·如何在Java程序中实现FTP的上传下
·Java应用:编写高级JavaScript应
·初学者学习java第一步——JDK环境
 文章评论

 精彩友情推荐
·Asp源码 PHP源码
·CGI源码 JSP源码
·建站书籍教程
·服务器软件 .net源码
·建站工具软件
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
ASP.NET ASP PHP JSP
·ASP.NET开发中的八个最佳实践09-21
·ASP.NET开发中的验证码技术09-18
·控件开发asp.net处理标签间内容09-18
·程序员成为成为编程高手的二十二条军规09-18
·VS 2005和ASP.NET 2.0中处理CSS样式表09-17
·如何有效监控.NET应用程序09-16
·浅析ASP.NET 2.0 Client Callback09-16
·探讨ASP.NETMVC框架内置AJAX支持编程技术09-15
·ASP.NET2.0的URL映射的实现方法09-13
·Java\.net\PHP比较 程序员眼中的.Net世界09-12
·asp.net中实现观察者设计模式09-12
·谈JSP与XML的交互09-01
·ASP连接11种数据库语法总结09-01
·如何利用ASP实现邮箱访问09-01
·ASP随机数的应用技术09-01
·定时使用DWRUtil.addRow生成表格08-27
·ASP申请单动态添加实现方法及代码08-27
·关于ASP中脚本执行顺序的讲解08-25
·ASP程序实现网页伪静态页源代码08-25
·ASP如何调用webservice08-22
·ASP实例:读取xml文件的程序08-14
·Asp组件检测函数08-11
·Linux系统下让PHP提高性能的工具APC05-06
·一个完整、安全的PHP用户登录系统11-14
·Apache+PHP+MySQL建立数据库驱动的动态网站08-24
·用SSH与PHP相连接 确保数据传输的安全性08-23
·PHP5手动最简安装方法08-03
·PHP程序加速探索之服务器负载测试07-11
·完全讲解PHP+MySQL的分页显示示例分析05-30
·用Suhosin加强PHP脚本语言安全性05-26
·初学入门 PHP 和 MySQL05-17
·传奇的诞生 PHP三位创始人简介05-10
·大型系统上PHP令人不爽的九大原因05-10
·JSTL中fn表达式的使用说明09-18
·JSP Web框架研究:Struts09-16
·JSF在GlassFish管理控制台中的应用09-12
·介绍JSP程序动态网站环境搭建的详细步骤09-12
·Jsp页面中文参数传递get和post方法分析09-12
·Java\.net\PHP比较 程序员眼中的.Net世界09-11
·JavaScript最常用的55个经典技巧09-08
·Windows操作系统下JSP程序开发环境配置09-03
·JSF点滴积累--权限验证09-03
·小小分页帮助程序08-29
·经验分享全过程JSP程序员完全蜕变手册08-27
  培训中心
人才交流中心 技术交流中心
  ITLab技术交流平台: