C++继承和成员函数指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/60000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ inheritance and member function pointers
提问by smh
In C++, can member function pointers be used to point to derived (or even base) class members?
在 C++ 中,可以使用成员函数指针指向派生(甚至基)类成员吗?
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
.
编辑:也许一个例子会有所帮助。假设我们有三个类的层次结构X
,Y
,Z
在继承顺序。
Y
因此有一个基类X
和一个派生类Z
。
Now we can define a member function pointer p
for class Y
. This is written as:
现在我们可以p
为 class定义一个成员函数指针Y
。这写成:
void (Y::*p)();
(For simplicity, I'll assume we're only interested in functions with the signature void f()
)
(为简单起见,我假设我们只对带有签名的函数感兴趣void f()
)
This pointer p
can now be used to point to member functions of class Y
.
这个指针p
现在可以用来指向 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
?
- 可以
p
用来指向派生类中的函数Z
吗? - 可以
p
用来指向基类中的函数X
吗?
采纳答案by outis
C++03 std, §4.11 2 Pointer to member conversions:
C++03 std, §4.11 2 指向成员转换的指针:
An rvalue of type “pointer to member of B of type cvT,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cvT,” 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 cvT,” 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*.
类型为“指向类型cvT的 B 成员的指针”的右值,其中 B 是类类型,可以转换为类型为“指向类型cvT的 D 成员的指针”的右值,其中 D 是派生类( B 的第 10 条。如果 B 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,则需要这种转换的程序是格式错误的。转换的结果引用与转换发生之前指向成员的指针相同的成员,但它引用基类成员,就好像它是派生类的成员一样。结果引用了 B 的 D 实例中的成员。由于结果的类型为“指向cv类型的 D 成员的指针”T,”它可以用 D 对象取消引用。结果与指向 B 成员的指针被 D 的 B 子对象解引用一样。空成员指针值被转换为目标类型的空成员指针值。52)
52)与指向对象的指针(从指针到派生到指向基类的指针)的规则相比,成员指针(从基类成员的指针到派生成员的指针)的转换规则似乎是相反的(4.10,第 10 条) . 这种反转对于确保类型安全是必要的。请注意,指向成员的指针不是指向对象的指针或指向函数的指针,并且此类指针的转换规则不适用于指向成员的指针。特别是,指向成员的指针不能转换为 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.99:
另一个方向(via static_cast
)的转换受§ 5.2.99 的约束:
An rvalue of type "pointer to member of D of type cv1T" can be converted to an rvalue of type "pointer to member of B of type cv2T", 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 cv2is 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.
“指向类型cv1T的 D 成员的指针”类型的右值可以转换为类型为“指向类型cv2T的 B 成员的指针”类型的右值,其中 B 是D的基类(第10 条 class.derived) ,如果存在从“指向 T 类型 B 成员的指针”到“指向 T 类型 D 成员的指针”的有效标准转换 ( 4.11 conv.mem),并且cv2与 cv-qualification 相同或更高资格比,cv1。11)空成员指针值(4.11 conv.mem) 转换为目标类型的空成员指针值。如果类 B 包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,转换的结果是未定义的。[注意:虽然B类不需要包含原成员,但解引用成员指针的对象的动态类型必须包含原成员;见5.5 expr.mptr.oper。]
11)函数类型(包括在指向成员函数类型的指针中使用的那些)永远不是 cv 限定的;见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.
总之,你可以从派生转换D::*
为基础B::*
,如果你可以从一个转换B::*
到D::*
,虽然你只能使用B::*
上,并且是类型d的或从D的后裔对象
回答by Matt Price
I'm not 100% sure what you are asking, but here is an example that works with virtual functions:
我不是 100% 确定你在问什么,但这里有一个适用于虚函数的例子:
#include <iostream>
using 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;
}
回答by CB Bailey
The critical issue with pointers to members is that they can be applied to any reference or pointer to a class of the correct type. This means that because Z
is derived from Y
a pointer (or reference) of type pointer (or reference) to Y
may actually point (or refer) to the base class sub-object of Z
or any other classderived from Y
.
成员指针的关键问题是它们可以应用于任何引用或指向正确类型的类的指针。这意味着,因为Z
派生自类型Y
为指针(或引用)的指针(或引用) toY
可能实际上指向(或引用)基类的子对象Z
或派生自的任何其他类Y
。
void (Y::*p)() = &Z::z_fn; // illegal
This means that anything assigned to a pointer to member of Y
must actually work with any Y
. If it was allowed to point to a member of Z
(that wasn't a member of Y
) then it would be possible to call a member function of Z
on some thing that wasn't actually a Z
.
这意味着分配给成员的指针的任何内容都Y
必须实际与 any 一起使用Y
。如果允许指向的成员Z
(不是 的成员Y
),那么就有可能Z
在某些实际上不是Z
.
On the other hand, any pointer to member of Y
also points the member of Z
(inheritance means that Z
has all the attributes and methods of its base) is it is legal to convert a pointer to member of Y
to a pointer to member of Z
. This is inherently safe.
另一方面,任何指向成员的指针Y
也指向成员的Z
(继承意味着Z
具有其基的所有属性和方法)是否将指向成员的指针转换为指向成员的指针是合法Y
的Z
。这本质上是安全的。
void (Y::*p)() = &Y::y_fn;
void (Z::*q)() = p; // legal and safe
回答by dagorym
You might want to check out this article Member Function Pointers and the Fastest Possible C++ DelegatesThe short answer seems to be yes, in some cases.
您可能想查看这篇文章Member Function Pointers and the Fastest possible C++ Delegates在某些情况下,简短的回答似乎是肯定的。
回答by smh
My experimentation revealed the following: Warning - this might be undefined behaviour. It would be helpful if someone could provide a definitive reference.
我的实验揭示了以下内容: 警告 - 这可能是未定义的行为。如果有人可以提供明确的参考,那将会很有帮助。
- This worked, but required a cast when assigning the derived member function to
p
. - This also worked, but required extra casts when dereferencing
p
.
- 这有效,但在将派生成员函数分配给
p
. - 这也有效,但在取消引用时需要额外的转换
p
。
If we're feeling really ambitious we could ask if p
can be used to point to member functions of unrelated classes. I didn't try it, but the FastDelegatepage linked in dagorym's answer suggests it's possible.
如果我们真的很有野心,我们可以问是否p
可以用来指向不相关类的成员函数。我没有尝试过,但是在 dagorym 的答案中链接的FastDelegate页面表明这是可能的。
In conclusion, I'll try to avoid using member function pointers in this way. Passages like the following don't inspire confidence:
总之,我将尽量避免以这种方式使用成员函数指针。像下面这样的段落不会激发信心:
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]
成员函数指针之间的转换是一个非常模糊的领域。在 C++ 的标准化过程中,关于是否应该能够将一个类的成员函数指针强制转换为基类或派生类的成员函数指针,以及是否可以在不相关的类之间进行转换,有很多讨论。到标准委员会下定决心时,不同的编译器供应商已经做出了实现决策,这些决策将他们锁定在对这些问题的不同答案中。[ FastDelegate 文章]
回答by Winston Ewert
Assume that we have class X, class Y : public X, and class Z : public Y
假设我们有 class X, class Y : public X, and class Z : public Y
You should be able to assign methods for both X, Y to pointers of type void (Y::*p)() but not methods for Z. To see why consider the following:
您应该能够将 X、Y 的方法分配给 void (Y::*p)() 类型的指针,但不能将 Z 的方法分配。要了解原因,请考虑以下事项:
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?
By allowing that assignment we permit the invocation of a method for Z on a Y object which could lead to who knows what. You can make it all work by casting the pointers but that is not safe or guaranteed to work.
通过允许该赋值,我们允许在 Y 对象上调用 Z 的方法,这可能导致谁知道什么。您可以通过转换指针使其全部工作,但这并不安全或保证工作。
回答by Steve Duitsman
I believe so. Since the function pointer uses the signature to identify itself, the base/derived behavior would rely on whatever object you called it on.
我相信是这样。由于函数指针使用签名来标识自身,因此基本/派生行为将依赖于您调用它的任何对象。
回答by Gena Batsyan
Here is an example of what works. You can override a method in derived class, and another method of base class that uses pointer to this overridden method indeed calls the derived class's method.
这是一个有效的例子。您可以覆盖派生类中的一个方法,而基类的另一个使用指向该覆盖方法的指针的方法确实调用了派生类的方法。
#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;
}