在C++中,可以使用成员函数指针指向派生(甚至是基类)类成员吗?
编辑:也许一个例子会有所帮助.假设我们有三个类的层次结构X
,Y
,Z
在继承顺序.
Y
因此有一个基类X
和一个派生类Z
.
现在我们可以p
为类定义一个成员函数指针Y
.这写成:
void (Y::*p)();
(为简单起见,我假设我们只对带签名的函数感兴趣void f()
)
此指针p
现在可用于指向类的成员函数Y
.
这个问题(两个问题,真的)是:
可以p
用来指向派生类中的函数Z
吗?
可以p
用来指向基类中的函数X
吗?
outis.. 28
C++ 03 std,§4.112指向成员转换的指针:
类型为" cv T 类型的B成员的指针"的r值,其中B是类类型,可以转换为类型为"指向类型为cv T 的D的成员的指针"的右值,其中D是派生类(如果B是不可访问的(第11条),D的模糊(10.2)或虚拟(10.1)基类,则需要这种转换的程序是不正确的.转换的结果引用与转换发生前指向成员的指针相同的成员,但它引用基类成员,就好像它是派生类的成员一样.结果引用D的D实例中的成员.由于结果具有类型"指向cv T 类型D的成员的指针",因此可以用D对象取消引用它.结果与使用B的B子对象取消引用B的成员的指针相同.将空成员指针值转换为目标类型的空成员指针值.52)
52)将指针转换为成员的指针(从指向基础成员的指针转换为指向派生成员的指针)与指向对象的指针(从指针到派生到指针到基础)的规则相比显得倒置(4.10,第10节) .这种反转对于确保类型安全是必要的.请注意,指向成员的指针不是指向对象的指针或指向函数的指针,并且此类指针的转换规则不适用于指向成员的指针.特别是,指向成员的指针不能转换为void*.
简而言之,只要成员不明确,就可以将指向可访问的非虚基类成员的指针转换为指向派生类成员的指针.
class A { public: void foo(); }; class B : public A {}; class C { public: void bar(); }; class D { public: void baz(); }; class E : public A, public B, private C, public virtual D { public: typedef void (E::*member)(); }; class F:public E { public: void bam(); }; ... int main() { E::member mbr; mbr = &A::foo; // invalid: ambiguous; E's A or B's A? mbr = &C::bar; // invalid: C is private mbr = &D::baz; // invalid: D is virtual mbr = &F::bam; // invalid: conversion isn't defined by the standard ...
另一方向(via static_cast
)的转换受第5.2.9节的约束9:
类型"指向Cv1 T 类型D的成员的指针"的右值可以转换为类型为"指向类型为cv2 T 的B的成员的指针"的右值,其中B是D 的基类(子句10 class.derived) ,如果存在从"指向T类型的B的成员的指针"到"指向T类型的D的成员的指针"的有效标准转换(4.11 conv.mem),并且cv2与cv-qualification相同,或者更大的cv-资格比,cv1.11)将空成员指针值(4.11 conv.mem)转换为目标类型的空成员指针值.如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员.否则,演员的结果是不确定的.[注意:虽然B类不需要包含原始成员,但是取消引用成员指针的对象的动态类型必须包含原始成员; 见5.5 expr.mptr.oper.]
11)函数类型(包括指向成员函数类型的指针中使用的函数类型)永远不会被cv限定; 见8.3.5 dcl.fct.
总之,你可以从派生转换D::*
为基础B::*
,如果你可以从一个转换B::*
到D::*
,虽然你只能使用B::*
上,并且是类型d的或从D的后裔对象
C++ 03 std,§4.112指向成员转换的指针:
类型为" cv T 类型的B成员的指针"的r值,其中B是类类型,可以转换为类型为"指向类型为cv T 的D的成员的指针"的右值,其中D是派生类(如果B是不可访问的(第11条),D的模糊(10.2)或虚拟(10.1)基类,则需要这种转换的程序是不正确的.转换的结果引用与转换发生前指向成员的指针相同的成员,但它引用基类成员,就好像它是派生类的成员一样.结果引用D的D实例中的成员.由于结果具有类型"指向cv T 类型D的成员的指针",因此可以用D对象取消引用它.结果与使用B的B子对象取消引用B的成员的指针相同.将空成员指针值转换为目标类型的空成员指针值.52)
52)将指针转换为成员的指针(从指向基础成员的指针转换为指向派生成员的指针)与指向对象的指针(从指针到派生到指针到基础)的规则相比显得倒置(4.10,第10节) .这种反转对于确保类型安全是必要的.请注意,指向成员的指针不是指向对象的指针或指向函数的指针,并且此类指针的转换规则不适用于指向成员的指针.特别是,指向成员的指针不能转换为void*.
简而言之,只要成员不明确,就可以将指向可访问的非虚基类成员的指针转换为指向派生类成员的指针.
class A { public: void foo(); }; class B : public A {}; class C { public: void bar(); }; class D { public: void baz(); }; class E : public A, public B, private C, public virtual D { public: typedef void (E::*member)(); }; class F:public E { public: void bam(); }; ... int main() { E::member mbr; mbr = &A::foo; // invalid: ambiguous; E's A or B's A? mbr = &C::bar; // invalid: C is private mbr = &D::baz; // invalid: D is virtual mbr = &F::bam; // invalid: conversion isn't defined by the standard ...
另一方向(via static_cast
)的转换受第5.2.9节的约束9:
类型"指向Cv1 T 类型D的成员的指针"的右值可以转换为类型为"指向类型为cv2 T 的B的成员的指针"的右值,其中B是D 的基类(子句10 class.derived) ,如果存在从"指向T类型的B的成员的指针"到"指向T类型的D的成员的指针"的有效标准转换(4.11 conv.mem),并且cv2与cv-qualification相同,或者更大的cv-资格比,cv1.11)将空成员指针值(4.11 conv.mem)转换为目标类型的空成员指针值.如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员.否则,演员的结果是不确定的.[注意:虽然B类不需要包含原始成员,但是取消引用成员指针的对象的动态类型必须包含原始成员; 见5.5 expr.mptr.oper.]
11)函数类型(包括指向成员函数类型的指针中使用的函数类型)永远不会被cv限定; 见8.3.5 dcl.fct.
总之,你可以从派生转换D::*
为基础B::*
,如果你可以从一个转换B::*
到D::*
,虽然你只能使用B::*
上,并且是类型d的或从D的后裔对象
我不是100%肯定你在问什么,但这是一个适用于虚函数的例子:
#includeusing namespace std; class A { public: virtual void foo() { cout << "A::foo\n"; } }; class B : public A { public: virtual void foo() { cout << "B::foo\n"; } }; int main() { void (A::*bar)() = &A::foo; (A().*bar)(); (B().*bar)(); return 0; }
指向成员的关键问题是它们可以应用于任何引用或指向正确类型的类的指针.这意味着因为Z
派生自Y
类型指针(或引用)的指针(或引用),Y
实际上可以指向(或引用)基类子对象Z
或从中派生的任何其他类Y
.
void (Y::*p)() = &Z::z_fn; // illegal
这意味着分配给指向成员的指针的任何内容Y
必须实际上与任何一个一起使用Y
.如果允许指向Z
(不是其成员Y
)的成员那么就可以调用某个Z
实际上不属于某个东西的成员函数Z
.
另一方面,任何指向成员的指针Y
也指向成员Z
(继承意味着Z
具有其基础的所有属性和方法)将指向成员的指针转换为指向成员的指针是合法Y
的Z
.这本质上是安全的.
void (Y::*p)() = &Y::y_fn; void (Z::*q)() = p; // legal and safe