C++ 转换成员函数指针
我需要使用一个成员函数指针,它接收在其他代码中使用的基类参数。好吧,我只想做下面的例子。这段代码运行得很好,但我想知道这样的强制转换是否总是安全的?我不能在这里进行C++ 转换成员函数指针,c++,pointers,casting,member-function-pointers,reinterpret-cast,C++,Pointers,Casting,Member Function Pointers,Reinterpret Cast,我需要使用一个成员函数指针,它接收在其他代码中使用的基类参数。好吧,我只想做下面的例子。这段代码运行得很好,但我想知道这样的强制转换是否总是安全的?我不能在这里进行动态或静态转换 #include <cstdio> class C { public:
动态
或静态
转换
#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;
}
#包括
C类
{
公众:
C():C('C'){}
虚拟~C(){}
常量字符c;
};
D类:公共C类
{
公众:
D():D('D'){}
虚拟~D(){}
常量字符d;
};
甲级
{
公众:
A(){}
虚拟~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[]
{
无效(A::*pf)(C&C)=&A:*f;
无效(A::*pg)(D&D)=重新解释铸件(&A::f);
A A;
C C;
D;
(a)pf(c);
(a.*pg)(d);
返回0;
}
您需要使用重新解释演员表
使其正常工作。在这种情况下,它应该是安全的(请参见备注),但如果使用多重继承,它可能会失败,因为当将D
作为C
传递时,需要调整指针。编译器需要知道这必须发生,这在本例中是不可能的(使用d
调用pg
只会跳过这一步,成员函数将获得d
对象的未修改地址)
备注:我说的是安全-实际上,它是未定义的行为,因为它将类型重新解释为一个不相关的类型并使用该类型,但它应该在大多数编译器上工作。请,请不要在生产代码中这样做。编译器应该拒绝使用您编写的动态转换的代码。(我认为这是一个打字错误,考虑到你的介绍文字,你的意思是重新解释演员阵容)
使用reinterpret_cast,您不会遇到定义良好的情况(这主要涉及转换为另一种类型,然后再转换回原始类型)。因此,对于强制转换的结果,我们处于未指定的领域,对于调用强制转换的结果时的行为,我们处于未定义的领域。否,您的示例不起作用。
首先,只能使用
dynamic\u cast
在相关类类型之间进行强制转换,而不能使用其他方法。其次,即使您将
动态\u cast
替换为重新解释\u cast
或C风格的cast(我认为您的意思),我也会得到以下输出:
cc 不是你想要的 为什么它能工作并且不会可怕地崩溃,是因为在成员函数指针之间来回转换是“安全的”,不会丢失任何信息。
它仍然打印某些内容的原因是编译器没有发现类型错误,但程序集不关心类型,它只关心地址,因此它仍然会调用
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):Cf(C&C):d “这是怎么回事?
D
甚至没有c
成员!”。嗯,又是因为地址。两个变量与此
指针的偏移量相同,即+0
。现在让我们将另一个成员放入C
(类简化):
然后再试一次,输出:
f(C&C):Cf(C&C):╠ 是的,我们开始了
C::C
现在位于偏移量+4
(+0
+sizeof int
),并且printf
从那里读取。在D
中,没有这样的偏移量,printf
从未初始化的内存中读取。另一方面,访问未初始化的内存是未定义的行为
因此,最后得出结论:不,这不安全 >你试图做的事情不能在C++中合法地进行。C++不支持函数参数类型上的任何一种协方差或逆变,无论这是一个成员函数还是一个自由函数。 在您的情况下,实现它的正确方法是引入一个用于参数类型转换的中间函数
class A
{
public:
...
void f( C& c ) { printf("%c\n",c.c); }
void f_with_D( D& d ) { f(d); }
...
};
让你的指针指向那个中间
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 );
class A
{
public:
...
void f( C& c ) { printf("%c\n",c.c); }
void f( D& d ) { f(static_cast<C&>(d)); }
...
};