我想做的事情有点模糊,所以让我举一个例子(这不是实际的代码):
template <class T>
class ArrayStorage {
protected:
void processStuff(void (ArrayStorage<T>::*procedure)(T *)) {
for (int i = 0; i < count; i++)
(this->*procedure)(content[i]);
}
// No method of type void (ArrayStorage<T>::*)(T *)
private:
T **content;
int count;
};
class DrawableStorage : public ArrayStorage<Drawable> {
public:
void drawStuff() {
processStuff((void (ArrayStorage<Drawable>::*)(Drawable *)) &DrawableStorage::drawOne);
}
private:
void drawOne(Drawable *item) {
item->draw();
}
};
基本上,有一个通用容器能够遍历其项目并在每个项目上使用一个方法(其指针在参数中)。但是,此方法不存在于此类中,而只存在于其子类中。您可以在子类“drawStuff”方法中看到,我提供了子类的方法指针,但是将其类型化为基类的方法。
令人惊讶的是,这已成功编译并且工作正常。
我的问题是,我的特定编译器是否能够处理它只是一个巧合,而事实上它完全无效,我应该摆脱它,或者它是否正确使用方法指针?
谢谢。
答案 0 :(得分:3)
这是指针可能的技巧之一。它的工作原理是因为DrawableStorage是一个ArrayStorage,并且函数头与预期的匹配(将指针指向与模板对象类型匹配的类型并返回void)。
一旦父对象具有指向函数的指针,即使它实际上没有将该函数作为其自身的成员,它也可以调用它。
虽然一开始读起来可能有点混乱,但它完全有效地使用了函数指针。
答案 1 :(得分:1)
以下是您示例的更简单版本:
#include <iostream>
using namespace std;
struct Arg
{
int i;
};
class A;
typedef void (A::*fncptr)(Arg&);
fncptr
是指向A
类的成员函数(方法)的指针,它通过引用获取Arg
。如果有人给你这种类型的非静态函数的地址,只有当你有一个类A
的对象时,你才能调用它(否则它必须是静态函数)。
class A
{
public:
A(Arg a) : a(a) { }
void process(fncptr f){ (this->*f)(a); }
protected:
Arg a;
};
class B : public A
{
public:
B(Arg a) : A(a) { }
void magic(){ process((fncptr)&B::function); }
void function(Arg& a) { cout << a.i; }
};
这是诀窍:因为你在函数process
中是类A
的成员函数,所以你不需要指向类A
的对象的指针来调用函数{{ 1}}因为你已经拥有一个:f
。因此,您可以调用此类或派生类的任何函数,并且只需要该函数的地址即可。
这是this
:
main()
输出:int main()
{
Arg a;
a.i = 71;
B* b = new B(a);
b->magic();
delete b;
}
答案 2 :(得分:1)
根据$ 5.2.9 / 12,这是合法的:
类型为“指向cv1 T类型D的成员的指针”的prvalue可以是 转换为cv2 T类型的“指向B成员的指针”的prvalue, 其中B是D的基类(第10条),如果是有效标准 从“指向T类型B的成员的指针”转换为“指向的指针” 类型T的D成员“存在(4.11),并且cv2是相同的 cv-quali fi cation,或者比cv1更高的cv-quali fi cation。 69无效 成员指针值(4.11)转换为空成员指针 目的地类型的值。如果B类包含原件 member,或者是包含该类的类的基类或派生类 原始成员,指向成员的结果指针指向 原始成员。否则,演员的结果是未定的。 [注意: 虽然B类不需要包含原始成员,但动态 必须是取消引用成员指针的对象的类型 包含原始成员;见5.5。 - 后注]
但是,如果您尝试使用多重继承或虚拟继承来执行此操作,那么事情就会开始中断,因为并非所有编译器都以标准兼容的方式实现成员函数指针。在MSVC和英特尔编译器上,所有成员函数指针的大小都不相同,因此您将失去关于转换的重要信息。
答案 3 :(得分:0)
基类没有拥有所请求的成员函数,所以没有办法解决这个问题。但是,您可以将基本功能改为模板:
template <typename S>
void processStuff(S * s, void (S::*procedure)(T *))
{
for (int i = 0; i < count; i++) { (s->*procedure)(content[i]); }
}
按如下方式调用它:
processStuff(this, &DrawableStorage::drawOne);