C++ inheritance and member function pointers
在C++中,可以使用成员函数指针指向派生的(甚至基数)类成员吗?
编辑:也许举个例子会有所帮助。假设我们按照继承的顺序有三个类:
现在我们可以为类
1 | void (Y::*p)(); |
(为了简单起见,我假设我们只对签名为
这个指针
这个问题(实际上是两个问题)是:
C++ 03 STD,第4.11条:指向成员转换的2指针:
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*.
号
简而言之,只要成员不含糊不清,就可以将指向可访问的非虚拟基类成员的指针转换为指向派生类成员的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 ... |
另一个方向的转换(通过
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.
号
简而言之,如果可以从
我不完全确定您的要求,但下面是一个使用虚拟功能的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> using namespace std; class A { public: virtual void foo() { cout <<"A::foo "; } }; class B : public A { public: virtual void foo() { cout <<"B::foo "; } }; int main() { void (A::*bar)() = &A::foo; (A().*bar)(); (B().*bar)(); return 0; } |
指向成员的指针的关键问题是,它们可以应用于任何引用或指向正确类型的类的指针。这意味着,由于
1 | void (Y::*p)() = &Z::z_fn; // illegal |
。
这意味着分配给
另一方面,指向
1 2 | void (Y::*p)() = &Y::y_fn; void (Z::*q)() = p; // legal and safe |
你可能想看看这篇文章的成员函数指针和最快的C++代表,简短的答案似乎是肯定的,在某些情况下。
假设我们有
您应该能够将x和y的方法赋给void(y::*p)()类型的指针,但不能赋给z类型的方法。要了解为什么要考虑以下内容:
1 2 3 | void (Y::*p)() = &Z::func; // we pretend this is legal Y * y = new Y; // clearly legal (y->*p)(); // okay, follows the rules, but what would this mean? |
号
通过允许该分配,我们允许在Y对象上调用z方法,这可能导致谁知道什么。你可以通过投射指针使一切正常工作,但这不安全,也不保证能正常工作。
我的实验揭示了以下几点:警告-这可能是未定义的行为。如果有人能提供明确的参考资料,那将是很有帮助的。
如果我们真的有雄心壮志,我们可以问一下,是否可以使用
总之,我将尝试避免以这种方式使用成员函数指针。像下面这样的段落不能激发信心:
Casting between member function
pointers is an extremely murky area.
During the standardization of C++,
there was a lot of discussion about
whether you should be able to cast a
member function pointer from one class
to a member function pointer of a base
or derived class, and whether you
could cast between unrelated classes.
By the time the standards committee
made up their mind, different compiler
vendors had already made
implementation decisions which had
locked them into different answers to
these questions. [FastDelegate article]
号
我相信是的。由于函数指针使用签名来标识自身,因此基/派生行为将依赖于您调用它的任何对象。
下面是一个什么有效的例子。可以重写派生类中的一个方法,并且使用指向此重写方法的指针的基类的另一个方法确实调用了派生类的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include <iostream> #include <string> using namespace std; class A { public: virtual void traverse(string arg) { find(&A::visit, arg); } protected: virtual void find(void (A::*method)(string arg), string arg) { (this->*method)(arg); } virtual void visit(string arg) { cout <<"A::visit, arg:" << arg << endl; } }; class B : public A { protected: virtual void visit(string arg) { cout <<"B::visit, arg:" << arg << endl; } }; int main() { A a; B b; a.traverse("one"); b.traverse("two"); return 0; } |
。