C++继承和成员函数指针
在C++中,可以使用成员函数指针指向派生(甚至基)类成员吗?
也许一个例子会有所帮助.假设我们有一个层次结构,按照继承顺序由 X
、Y
、Z
三个类组成.Y
因此有一个基类 X
和一个派生类 Z
.
现在我们可以为类Y
定义一个成员函数指针p
.写成:
void (Y::*p)();
(为简单起见,我假设我们只对签名为 void f()
的函数感兴趣)
这个指针p
现在可以用来指向Y
类的成员函数.
这个问题(实际上是两个问题)是:
- 可以使用
p
来指向派生类Z
中的函数吗? p
可以用来指向基类X
中的一个函数吗?
C++03 std, §4.11 2 指向成员转换的指??针:
<块引用>类型为指向 cv T 类型的 B 成员的指针"类型的右值,其中 B 是一个类类型,可以转换为类型为指向类型
52)与指向对象的指针(从指针到派生到指向基的指针)(4.1??0,第 10 条).这种反转对于确保类型安全是必要的.请注意,指向成员的指针不是指向对象的指针或指向函数的指针,并且此类指针的转换规则不适用于指向成员的指针.特别是,指向成员的指针不能转换为 void*.
简而言之,您可以将指向可访问的非虚拟基类成员的指针转换为指向派生类成员的指针,只要该成员没有歧义即可.
class A {上市:无效的 foo();};B 类:公共 A {};C类{上市:空栏();};D类{上市:无效巴兹();};E 类:公共 A、公共 B、私有 C、公共虚拟 D {上市:typedef void (E::*member)();};F类:公共E{上市:无效的巴姆();};...int main() {E::会员mbr;mbr = &A::foo;//无效:模棱两可;E是A还是B是A?mbr = &C::bar;//无效:C 是私有的mbr = &D::baz;//无效:D 是虚拟的mbr = &F::bam;//无效:转换不是由标准定义的...
另一个方向的转换(通过 static_cast
)由 § 5.2.9 9:
指向类型 cv1 T 的 D 成员的指针"类型的右值可以转换为指向类型 cv2 T 的 B 成员的指针"类型的右值",其中 B 是基类(子句 10 class.derived) 的 D,如果存在从指向 T 类型 B 成员的指针"到指向 T 类型 D 成员的指针"的有效标准转换(4.11 conv.mem),并且 cv2 是与 cv1 相同或更高的 cv 限定.11) null 成员指针值 (4.11 conv.mem) 转换为目标类型的空成员指针值.如果类 B 包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员.否则,转换的结果是未定义的.[注意:虽然B类不需要包含原成员,但解引用成员指针的对象的动态类型必须包含原成员;见 5.5 expr.mptr.oper.]
11) 函数类型(包括用于成员函数的指针类型)永远不会被 cv 限定;参见 8.3.5 dcl.fct.
简而言之,如果您可以从 B::* 转换,则可以从派生的
>D::*
转换为基本的 B::*
到 D::*
,但您只能在 D 类型或从 D 派生的对象上使用 B::*
.
In C++, can member function pointers be used to point to derived (or even base) class members?
EDIT:
Perhaps an example will help. Suppose we have a hierarchy of three classes X
, Y
, Z
in order of inheritance.
Y
therefore has a base class X
and a derived class Z
.
Now we can define a member function pointer p
for class Y
. This is written as:
void (Y::*p)();
(For simplicity, I'll assume we're only interested in functions with the signature void f()
)
This pointer p
can now be used to point to member functions of class Y
.
This question (two questions, really) is then:
- Can
p
be used to point to a function in the derived classZ
? - Can
p
be used to point to a function in the base classX
?
解决方案
C++03 std, §4.11 2 Pointer to member conversions:
An rvalue of type "pointer to member of B of type cv T," where B is a class type, can be converted to an rvalue of type "pointer to member of D of type cv T," where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type "pointer to member of D of type cv T," it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D. The null member pointer value is converted to the null member pointer value of the destination type. 52)
52)The rule for conversion of pointers to members (from pointer to member of base to pointer to member of derived) appears inverted compared to the rule for pointers to objects (from pointer to derived to pointer to base) (4.10, clause 10). This inversion is necessary to ensure type safety. Note that a pointer to member is not a pointer to object or a pointer to function and the rules for conversions of such pointers do not apply to pointers to members. In particular, a pointer to member cannot be converted to a void*.
In short, you can convert a pointer to a member of an accessible, non-virtual base class to a pointer to a member of a derived class as long as the member isn't ambiguous.
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
...
Conversion in the other direction (via static_cast
) is governed by § 5.2.9 9:
An rvalue of type "pointer to member of D of type cv1 T" can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class (clause 10 class.derived) of D, if a valid standard conversion from "pointer to member of B of type T" to "pointer to member of D of type T" exists (4.11 conv.mem), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.11) The null member pointer value (4.11 conv.mem) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5 expr.mptr.oper.]
11) Function types (including those used in pointer to member function types) are never cv-qualified; see 8.3.5 dcl.fct.
In short, you can convert from a derived D::*
to a base B::*
if you can convert from a B::*
to a D::*
, though you can only use the B::*
on objects that are of type D or are descended from D.
相关文章