C++ 多重继承和 vtable

2024-04-04

因此,回到基础知识,我试图将我的注意力集中在 vtables 和诸如此类的事情上。在下面的例子中,如果我要传递一个B*对于某个函数,该函数如何知道调用该函数的 vtable 中的方法C对象而不是 vtable 中的方法A?是否有两个单独的 VTable 传递给该对象?接口指针真的只是虚函数表吗(因为接口 IIRC 不能包含属性声明)?

我想说的是,在我实际尝试这段代码之前,我一直假设您不能一次继承多个接口/类(并且所有接口都必须是线性的,可以这么说),以便 vtable 构建在自身之上。

如果我对虚拟表如何工作的想法was正确(我现在知道不是),然后传递一个B*并打电话给B::OutB()会打电话的A:OutA()相反(显然情况并非如此)。

有人可以透露一些信息吗?

// Includes
#include <windows.h>
#include <iostream>

interface A
{
public:
    virtual void OutA() = 0;
};

interface B
{
public:
    virtual void OutB() = 0;
};

class C : public A, public B
{
public:
    void OutA();
    void OutB();
};

void C::OutA()
{
    printf("Out A\n");
}

void C::OutB()
{
    printf("Out B\n");
}

int main()
{
    C obj;
    obj.OutA();
    obj.OutB();

    A* ap = (A*)&obj;
    B* bp = (B*)&obj;

    ap->OutA();
    bp->OutB();

    system("pause");

    // Return
    return 0;
}

输出(如预期):

Out A
Out B
Out A
Out B

我不知道什么是interface是,因为:

  • interface is notC++ 关键字;
  • C++语义中没有“接口”的概念;
  • 不同的 C++ 习惯用法或模式可以将接口一词用于不同的特定目的;
  • 其他语言使用“接口”来描述完全不同的实体(在 Java 中,它就像某种特殊的有限基类,在 O'Caml 中,它用于在 C++ 中可能使用模板概念的地方)。

但如果你正在编写 C++ 并且A and B上课了,然后C将包含两个子对象:A and B,并且每个子对象都有自己的 vtable 指针。

将 C++ 编译为 C 时,我们可以:

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

const int debug = 0;

void __pure_virtual_called() {
    fputs ("pure virtual function called\n", stderr);
    abort();
}

/* Translation of:

class A
{
public:
    virtual void OutA() = 0;
};
*/

struct A;

typedef struct  {
    void (*ptr__OutA) (struct A *__this);
} vtable__A;

typedef struct A {
    vtable__A *__vptr;
} A;

/* translation A::OutA() 
 * pure virtual function */
void A__OutA (A *__this) {
     __pure_virtual_called();
}

vtable__A vtable__A__A = { .ptr__OutA = A__OutA };

void A__constructor (A *__this) {
    if (debug)
        printf ("A__constructor %p\n", (void*)__this);

    /* dynamic type is initialised to A */
    __this->__vptr = &vtable__A__A;
}

/* Translation of:

class B
{
public:
    virtual void OutB() = 0;
};

*/

struct B;

typedef struct {
    void (*ptr__OutB)(struct B *__this);
} vtable__B;

typedef struct B {
    vtable__B *__vptr;
} B;

/* translation B::OutB() 
 * pure virtual function */
void B__OutB (B *__this) {
     __pure_virtual_called();
}

vtable__B vtable__B__B = { .ptr__OutB = B__OutB };

void B__constructor (B *__this) {
    if (debug)
        printf ("B__constructor %p\n", (void*)__this);

    /* dynamic type is initialised to B */
    __this->__vptr = &vtable__B__B;
}
/* Translation of:

class C : public A, public B
{
public:
    void OutA(); // overrides A::OutA()
    void OutB(); // overrides B::OutB()
    // note :
    // no new virtual function
};

*/

/* no new virtual function 
 * so no specific vtable type! */

typedef struct {
/* no additional vptr, we already have 2! */
    A base__A;
    B base__B;
} C;

/******* upcasts 
 * translation of 
 * static_cast<C*> (p) 
 */

/* translation of 
 * A *p;
 * static_cast<C*> (p);
 */
C *static_cast__A__C (A *__ptr) {
    /* 
     * base__A is first member of C
     * so offsetof(C, base__A) == 0
     * can skip the pointer adjustment
     */ 
    return (C*)__ptr;
}

/* translation of 
 * B *p;
 * static_cast<C*> (p);
 */
C *static_cast__B__C (B *__ptr) {
    /* locate enclosing C object: 
     * __base__B is not first member
     * need to adjust pointer
     */
    return (C*)((char*)__ptr - offsetof(C, base__B));
}

/* translation of virtual functions of C 
 * overriding function declarations from A
 */

/* translation of C::OutA() */

/* C::OutA() called from C */
void C__OutA (C *__this) {
    printf("Out A this=%p\n", (void*)__this);
}

/* C::OutA() called from A */
void C__A__OutA (A *__this) {
    if (debug)
            printf ("C__A__OutA %p\n", (void*)__this);
    C__OutA (static_cast__A__C (__this));
}

vtable__A vtable__A__C = { .ptr__OutA = C__A__OutA };

/* translation of virtual functions of C 
 * overriding function declarations from B
 */

/* translation of C::OutB() */

/* C::OutB() called from C */
void C__OutB (C *__this) {
    printf("Out B this=%p\n", (void*)__this);
}

/* C::OutB() called from B */
void C__B__OutB (B *__this) {
    if (debug)
            printf ("C__B__OutB %p\n", (void*)__this);
    C__OutB (static_cast__B__C (__this));
}

vtable__B vtable__B__C = { .ptr__OutB = C__B__OutB };

void C__constructor (C *__this) {
    if (debug)
        printf ("C__constructor %p\n", (void*)__this);
    /* construct subobjects */
    A__constructor (&__this->base__A);
    B__constructor (&__this->base__B);

    /* adjust dynamic type of this to C */
    __this->base__A.__vptr = &vtable__A__C;
    __this->base__B.__vptr = &vtable__B__C;
}

/* calls to C virtual functions with a C* 
 */

/* translation of 
 * C *p;
 * p->OutA();
 *
 * is
 * ((A*)p)->OutA();
 *
 * because C::OutA() is overrides A::OutA()
 */
void dyn__C__OutA (C *__this) {
    A *base_ptr__A = &__this->base__A;
    base_ptr__A->__vptr->ptr__OutA (base_ptr__A);
}

/* translation of 

int main()
{
    C obj;
    obj.OutA();
    obj.OutB();

    A *ap = &obj;
    B *bp = &obj;
    C *cp = &obj;

    ap->OutA();
    bp->OutB();
    cp->OutA();

    // Return
    return 0;
}

 *
 */

int main () {
    /* translation of:
    C obj; 
    */
    C obj;
    C__constructor (&obj);

    /* translation of:
    obj.OutA();
    obj.OutB();
     * obj is a locally declared object
     * so dynamic type of obj is known as C
     * can make direct call to C::OutA(), C::OutB()
     */
    C__OutA (&obj);
    C__OutB (&obj);

    /* dumb (zero optimisation) translation of:
    A *ap = &obj;
    B *bp = &obj;
    C *cp = &obj;
    */
    A *ap = &obj.base__A;
    B *bp = &obj.base__B;
    C *cp = &obj;

    /* translation of:
    ap->OutA();
    bp->OutB();
    cp->OutA();

    * dumb compiler = no optimisation
    * so dynamic type of *ap, *bp, *cp is unknown
    * so make "virtual" calls using vtable
    */
    ap->__vptr->ptr__OutA(ap);
    bp->__vptr->ptr__OutB(bp);
    dyn__C__OutA (cp);

    /* note: obj lifetime ends now
     * C has a trivial destructor 
     * so no destructor call needed
     */

    return 0;
}

See http://ideone.com/TioyX http://ideone.com/TioyX

Output:

Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 多重继承和 vtable 的相关文章

  • 为什么使用abs()或fabs()而不是条件否定?

    在 C C 中 为什么要使用abs or fabs 不使用以下代码即可查找变量的绝对值 int absoluteValue value lt 0 value value 这与较低级别的指令较少有关吗 您提出的 有条件的abs 并不等于std
  • 添加对共享类的多个 WCF 服务的服务引用

    我正在尝试将我的 WCF Web 服务拆分为几个服务 而不是一个巨大的服务 但是 Visual Studio Silverlight 客户端 复制了两个服务共享的公共类 这是一个简单的例子来说明我的问题 在此示例中 有两个服务 两者都返回类
  • 处理 fanart.tv Web 服务响应 JSON 和 C#

    我正在尝试使用 fanart tv Webservice API 但有几个问题 我正在使用 Json Net Newtonsoft Json 并通过其他 Web 服务将 JSON 响应直接反序列化为 C 对象 这里的问题是元素名称正在更改
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • 获取从属性构造函数内部应用到哪个属性的成员?

    我有一个自定义属性 在自定义属性的构造函数内 我想将属性的属性值设置为属性所应用到的属性的类型 是否有某种方式可以访问该属性所应用到的成员从我的属性类内部 可以从 NET 4 5 using CallerMemberName Somethi
  • 为什么密码错误会导致“填充无效且无法删除”?

    我需要一些简单的字符串加密 所以我编写了以下代码 有很多 灵感 来自here http www codeproject com KB security DotNetCrypto aspx create and initialize a cr
  • C++派生模板类继承自模板基类,无法调用基类构造函数[重复]

    这个问题在这里已经有答案了 我试图从基类 模板 继承 派生类也是模板 它们具有相同的类型 T 我收到编译错误 非法成员初始化 Base 不是基类或成员 为什么 如何调用基类构造函数 include
  • 为什么 FTPWebRequest 或 WebRequest 通常不接受 /../ 路径?

    我正在尝试从 ftp Web 服务器自动执行一些上传 下载任务 当我通过客户端甚至通过 Firefox 连接到服务器时 为了访问我的目录 我必须指定如下路径 ftp ftpserver com AB00000 incoming files
  • C# 创建数组的数组

    我正在尝试创建一个将使用重复数据的数组数组 如下所示 int list1 new int 4 1 2 3 4 int list2 new int 4 5 6 7 8 int list3 new int 4 1 3 2 1 int list4
  • UWP 无法在两个应用程序之间创建本地主机连接

    我正在尝试在两个 UWP 应用程序之间设置 TCP 连接 当服务器和客户端在同一个应用程序中运行时 它可以正常工作 但是 当我将服务器部分移动到一个应用程序并将客户端部分移动到另一个应用程序时 ConnectAsync 会引发异常 服务器未
  • Qt - 设置不可编辑的QComboBox的显示文本

    我想将 QComboBox 的文本设置为某些自定义文本 不在 QComboBox 的列表中 而不将此文本添加为 QComboBox 的项目 此行为可以在可编辑的 QComboBox 上实现QComboBox setEditText cons
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 是否有一个 C++ 库可以从 PDF 文件中提取文本,例如 PDFBox for Java? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 去年 我使用 PDFBox 在 Java 中创建了一个应用程序来获取某些 PDF 文件中的原始文本 现在
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • 如何检测 C# 中该字典键是否存在?

    我正在使用 Exchange Web 服务托管 API 和联系人数据 我有以下代码 即功能性的 但并不理想 foreach Contact c in contactList string openItemUrl https service
  • 内核开发和 C++ [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 从我know https stackoverflow com questions 580292 what languages are windo
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因
  • 如何在 GCC 5 中处理双 ABI?

    我尝试了解如何克服 GCC 5 中引入的双重 ABI 的问题 但是 我没能做到 这是一个重现错误的非常简单的示例 我使用的GCC版本是5 2 如您所见 我的主要函数 在 main cpp 文件中 非常简单 main cpp include
  • boost::program_options:带有固定和可变标记的参数?

    是否可以在 boost program options 中使用此类参数 program p1 123 p2 234 p3 345 p12 678 即 是否可以使用第一个标记指定参数名称 例如 p 后跟一个数字 是动态的吗 我想避免这种情况

随机推荐