Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;多重继承+;虚拟函数(-fulligence)=奇怪的行为(也包括函数指针)_C++_Pointers_Inheritance_Casting_Static Cast - Fatal编程技术网

C++ C++;多重继承+;虚拟函数(-fulligence)=奇怪的行为(也包括函数指针)

C++ C++;多重继承+;虚拟函数(-fulligence)=奇怪的行为(也包括函数指针),c++,pointers,inheritance,casting,static-cast,C++,Pointers,Inheritance,Casting,Static Cast,我正在创建两个接口,旨在提供对回调功能的访问。也就是说,从接口A继承允许类使用类型1的回调;接口B允许类型2。从A和B继承允许两种类型的回调。最终的目的是,类A和类B将通过继承它们来处理所有的脏活 第一个问题 下面是一个小例子,可以说明我遇到的一些麻烦: class A { public: static void AFoo( void* inst ) { ((A*)inst)->ABar( ); } virtual void ABar( vo

我正在创建两个接口,旨在提供对回调功能的访问。也就是说,从接口A继承允许类使用类型1的回调;接口B允许类型2。从A和B继承允许两种类型的回调。最终的目的是,类A和类B将通过继承它们来处理所有的脏活

第一个问题 下面是一个小例子,可以说明我遇到的一些麻烦:

class A
{
public:
    static void AFoo( void* inst )
    {
        ((A*)inst)->ABar( );
    }
    virtual void ABar( void ) = 0;
};

class B
{
public:
    static void BFoo( void* inst )
    {
        ((B*)inst)->BBar( );
    }
    virtual void BBar( void ) = 0;
};

class C : public A, public B
{
public:
    void ABar( void ){ cout << "A"; };
    void BBar( void ){ cout << "B"; };
};
我希望得到“AB”作为输出。取而代之的是“AA”。颠倒派生类的顺序(B在A之前),生成“BB”。为什么会这样

第二个问题 我使用的实际接口是模板化的,因此代码看起来更像

template <class T> class A
{
public:
    static void AFoo( void* inst )
    {
        ((T*)inst)->ABar( );
    }
    virtual void ABar( void ) = 0;
};

template <class T> class B
{
public:
    static void BFoo( void* inst )
    {
        ((T*)inst)->BBar( );
    }
    virtual void BBar( void ) = 0;
};

class C : public A<C>, public B<C>
{
public:
    void ABar( void ){ cout << "A"; };
    void BBar( void ){ cout << "B"; };
};
产生正确的输出:“AB”

这个小例子在这里效果很好,但在实践中并不总是正确的。非常奇怪的事情开始发生,类似于上面第一个问题中的奇怪。主要的问题似乎是两个虚拟函数(或静态函数,或其他什么)并不总是能进入C

例如,我可以成功地调用C::AFoo(),但并不总是调用C::BFoo()。这有时取决于我从A和B派生的顺序:
类C:public A,public B
可能会在AFoo和BFoo都不起作用的情况下生成代码,而
类C:public B,public A
可能会在其中一个或两个都起作用的情况下生成代码

由于类是模板化的,我可以删除A和B中的虚拟函数。这样做会产生工作代码,当然,只要C中存在ABar和BBar。这是可以接受的,但不是期望的;我宁愿知道问题出在哪里

上面的代码可能导致奇怪问题的原因有哪些


为什么第二个示例会产生正确的输出,而第一个示例却没有?

您正在调用未定义的行为。您可以将
X*
强制转换为
void*
,但一旦您这样做,将
void*
强制转换为
X*
是唯一安全的方法(这并不完全正确,我过于简化了,但为了论证起见,假设它是)

现在为什么代码的行为是这样的?实现MI的一种方法类似于:

 struct A
 {
    A_vtable* vtbl;
 };

 struct B
 {
    B_vtable* vtbl;
 };

 struct C
 {
    struct A;
    struct B;
 };

在本例中,A是第一个,但顺序将由编译器确定。当您强制转换为void时,您会得到一个指向C开头的指针。当您将该void*强制转换回void*时,您已经丢失了必要时适当调整指针所需的信息。由于A和B都有一个具有相同签名的虚拟函数,因此最终调用impl。不管哪个类恰好是对象布局中的第一个。

正如Logan Capaldo所说,这种回调实现方法存在问题。将void*强制转换为XXX*是不安全的,因为我们无法保证强制转换的指针实际上指向XXX(它可能指向其他类型,甚至是无效的地址,这将导致无法预料的问题)。我的建议是将静态函数的参数类型更改为接口类型,即:

class A
{
public:
    static void AFoo( A* inst )
    {
        inst->ABar( );
    }
    virtual void ABar( void ) = 0;
};

class B
{
public:
    static void BFoo( B* inst )
    {
         inst->BBar( );
    }
    virtual void BBar( void ) = 0;
};

class C : public A, public B
{
public:
    void ABar( void ){ cout << "A"; };
    void BBar( void ){ cout << "B"; };
};


C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo(c_inst );
BFoo(c_inst );
A类
{
公众:
静态无效AFoo(A*仪表)
{
inst->ABar();
}
虚空ABar(void)=0;
};
B类
{
公众:
静态空隙BFoo(B*inst)
{
仪器->BBar();
}
虚空BBar(void)=0;
};
C类:公共A、公共B
{
公众:

void ABar(void){我是否应该记得将C强制转换为基类,这是继承的主要好处。我仍然强烈建议您将静态函数的参数类型更改为指向接口类型的指针(a*,B*…)根据您的实现,您希望用户传递的不是void*,而是A*或B*,而不是其他任何东西(void*或int*,char*)。“void*”参数类型会让静态函数的用户感到困惑,而且他们也可能忘记像您这样将C强制转换为基类。
 struct A
 {
    A_vtable* vtbl;
 };

 struct B
 {
    B_vtable* vtbl;
 };

 struct C
 {
    struct A;
    struct B;
 };
class A
{
public:
    static void AFoo( A* inst )
    {
        inst->ABar( );
    }
    virtual void ABar( void ) = 0;
};

class B
{
public:
    static void BFoo( B* inst )
    {
         inst->BBar( );
    }
    virtual void BBar( void ) = 0;
};

class C : public A, public B
{
public:
    void ABar( void ){ cout << "A"; };
    void BBar( void ){ cout << "B"; };
};


C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo(c_inst );
BFoo(c_inst );