C ++多重继承和vtables [英] C++ multiple inheritance and vtables

查看:95
本文介绍了C ++多重继承和vtables的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,回到基础知识,我想把头放在vtables之类的东西上.在下面的示例中,如果我想将B*传递给某个函数,该函数如何知道调用C对象的vtable中的方法,而不是调用A的vtable中的方法? ?是否有两个单独的VTable传递给该对象?接口指针真的只是vtable(因为接口,IIRC不能包含属性声明)吗?

我要说的是,直到我实际尝试这段代码之前,我一直以为您不能一次继承多个接口/类(并且所有接口必须继承).可以说是线性的),以便将vtable建立在其自身上.

如果我对vtables是如何工作的想法 是正确的(我现在知道不是),那么传递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不是 一个C ++关键字;
  • 在C ++语义中没有接口"的概念;
  • 不同的C ++习惯用法或模式可以将word接口用于不同的特定目的;
  • 其他语言使用接口"来描述完全不同的实体(在Java中,这就像是一种特殊的有限基类,在O'Caml中,它用在C ++中可能使用模板概念的地方).

但是,如果您正在编写C ++并且AB是类,则C将包含两个子对象:AB,并且这些子对象中的每一个都有其自己的vtable指针. /p>

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

请参见 http://ideone.com/TioyX

输出:

Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec

So going back to basics, I'm trying to wrap my head around vtables and whatnot. In the following example, if I were to, say, pass a B* to some function, how does that function know to call the methods in the vtable of the C object instead of the methods from the vtable of A? Are there two, separate VTables that are passed to that object? Are interface pointers really just vtables (since interfaces, IIRC, cannot contain property declarations)?

What I'm trying to say is, up until I actually tried this code, I was under the assumption you couldn't inherit off of more than one interface/class at a time (and that all of the interfaces had to be linear, so to speak) so that the vtable built onto itself.

If my idea of how vtables worked was correct (which I now know it is not), then passing a B* and called B::OutB() would have called A:OutA() instead (which is obviously not the case).

Can someone shed some light?

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

outputs (as expected):

Out A
Out B
Out A
Out B

解决方案

I have no idea what an interface is, because:

  • interface is not a C++ keyword;
  • there is no concept of an "interface" in C++ semantics;
  • different C++ idioms or patterns can use the word interface for different specific purposes;
  • other languages use "interface" to describe completely different entities (in Java it's like sort of special limited base class, in O'Caml it's used where in C++ you might use a template concept).

But if you were writing C++ and A and B were classes, then C would contain two subobjects: A and B, and each of these subobject would have its own vtable pointer.

When compiling C++ to C, we could have:

#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

Output:

Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec
Out B this=0xbfeee2ec
Out A this=0xbfeee2ec

这篇关于C ++多重继承和vtables的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆