5

我需要使用一个成员函数指针,它接受在其他代码中使用的基类参数。那么,简单地说,我想要做的[某事]像下面的例子。此代码工作正常,但我不知道这样的演员总是安全吗?我不能在这里施放dynamicstatic铸造成员函数指针

#include <cstdio>             

class C 
{               
public:                
     C() : c('c') {}            
     virtual ~C() {}            

     const char c;            
};                 

class D : public C 
{             
public:                
     D() : d('d') {}            
     virtual ~D() {}            

     const char d;            
};                 

class A 
{               
public:                
     A() {}              
     virtual ~A() {}            

     void f(C& c) { printf("%c\n",c.c); }      
     void g(D& d) { printf("%c %c\n",d.c,d.d); }    
};                 

int main (int argc, char const* argv[])        
{                 
     void (A::*pf)(C& c) = &A::f;        
     void (A::*pg)(D& d) = reinterpret_cast<void (A::*)(D&)>(&A::f); 

     A a;               
     C c;               
     D d;               

     (a.*pf)(c);            
     (a.*pg)(d);            

     return 0;             
}                
+3

你说的代码工作正常,但它甚至不为我编译。 – Xeo 2011-05-31 18:48:47

+1

它编译和(似乎)与'reinterpret_cast'一起工作;我想问题是,这是安全的吗? – 2011-05-31 18:51:35

+0

感觉f和g实际上是虚拟的吗?如果是这样的话,你可以在一些从A派生的类中重写它们。比铸造好得多。 – 2011-05-31 19:15:22

回答

2

你想要做的事情不能在C++中合法地完成。不管这是成员函数还是自由函数,C++都不支持函数参数类型的任何类型的协方差或对数方差。

在您的情况来实现它的正确方法是引入了参数类型转换目的的中间功能

class A 
{               
public:   
    ...             
    void f(C& c) { printf("%c\n",c.c); }      
    void f_with_D(D& d) { f(d); } 
    ... 
};   

,让你的指针指向的是中间的功能没有任何强制转换

void (A::*pg)(D& d) = &A::f_with_D; 

现在

A a; 
D d;               
(a.*pg)(d); 

最终将CA将a.fC对象的子对象d作为参数。

编辑:是的,它将与功能过载(如果我正确理解你的问题)。你一定要记住,以便直接向函数的正确版本的内部通话功能超载,你必须使用显式类型转换

class A 
{               
public:   
    ...             
    void f(C& c) { printf("%c\n",c.c); }      
    void f(D& d) { f(static_cast<C&>(d)); } 
    ... 
};   

没有你结了投A::f(D&)递归调用自己。

+0

不幸的是,有很多Ds类,我希望编译器选择适当的类。它会与功能超负荷工作? – qba 2011-05-31 21:10:31

+0

不,不会。所以结论是我需要完全不同的解决方案。 – qba 2011-05-31 21:30:00

+0

它的工作原理,谢谢。目前只有在测试代码不生产一个,但有一个希望:)在生产代码中,我得到'错误:无效static_cast从类型'<无法解析的重载函数类型>''。也许还有别的东西搞乱了(static_cast是由库完成的)。 – qba 2011-06-01 06:37:46

0

您需要使用reinterpret_cast才能使其工作。在这种情况下它应该是安全的(请参见备注),但如果使用多重继承,则可能会失败,因为在将D作为C传递时,需要调整指针。编译器需要知道这是必须发生的,在这种情况下这是不可能的(调用pgd只会跳过这一步,成员函数会得到D对象的未修改地址)。

备注:我说安全 - 嗯,实际上它是不确定的行为由于reinterpret_cast'ing一个类型无关的类型和使用该类型,但它应该在大多数编译器仍然工作。请不要在生产代码中使用它。

1

编译器应该用你写的dynamic_cast来拒绝代码。 (我认为这是一个错字,考虑到你的介绍文本,你的意思是reinterpret_cast)。

随着reinterpret_cast,你不在一个明确定义的情况下(主要涉及到转换为另一种类型,然后回到原来的情况)。所以我们处于未指定领域的演员结果,以及在调用演员结果时的未定义领域。

1

不,您的示例无法正常工作。
首先,您只能使用dynamic_cast相关类别类型之间进行投射,而不是其他。
其次,即使你更换dynamic_castreinterpret_cast或C样式转换(我假设你的意思),然后我得到以下输出:

c
c

没有真正想要的东西。

为什么即使有效,也不会发生可怕的崩溃是因为在成员函数指针之间来回转换是“安全”的,所以不会丢失任何信息。
为什么它仍然打印一些东西是因为编译器没有发现类型错误,但程序集不关心类型,它只关心地址,所以它仍然会调用A::f,因为这是您保存的指针,无论的类型。

有趣的是,即使您没有关联类(D不会继承自C),这仍然有效,因为程序集不关心类型。改变A中的函数以下列方式:

void f(C& c) { printf("f(C& c): %c\n",c.c); } 
void g(D& d) { printf("g(D& d): %c\n",d.d); } 

导致以下的输出:

f(C& c): c
f(C& c): d

“?如何运作的D甚至没有一个c成员!”。那么,再次因为地址。两个变量都与this指针相同,即+0。现在,让我们把另一成员C(类简化):

struct C{ 
     C() : c('c') {} 
     int i; // mean 
     const char c; 
}; 

,并再次尝试的是,输出:

f(C& c): c
f(C& c): ╠

是的,我们走吧。 C::c现在在偏移量+4+0 + sizeof int)和printf从那里读取。在D中,没有这样的偏移量,并且从未初始化的存储器读取printf。另一方面,访问未初始化的内存是未定义的行为

因此,最后得出结论:不,这是不安全的。 :)

+0

好吧,reinterpret_cast的结果是我真正想要的。 pg是函数f的指针,所以它应该调用函数f。它还能是什么? – qba 2011-05-31 21:05:32

+0

@qba:那你为什么称它为'pg'并且在'A'中有一个'g'功能?如果你想调用'f'函数,为什么在你的代码中包含'g'? – Xeo 2011-05-31 21:12:33

+0

另一个例子复制和粘贴有多糟糕:P我正在用这些函数测试C++,并且我忘记删除'g'。 – qba 2011-05-31 21:26:53