5

void(Derived::*)()对我来说,将 a转换为 a看起来非常安全void(Base::*)(),就像在这段代码中一样:

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    void(Base::*any_method)();
    void call_it(){
        (this->*any_method)();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Base& a=*new Derived;
    a.any_method=&Derived::a_method;
    a.call_it();
}

但是编译器抱怨在a.any_method=&Derived::a_method;. 这是防止微妙的编程错误的障碍,还是只是让编译器编写者的生活更轻松?是否有解决方法让Base类有一个指向Derived 没有类型知识的成员函数的指针(也就是说,我不能Base使用模板参数制作模板Derived)。

4

4 回答 4

7

如果您Derived::a_method()尝试使用仅存在于Derived中而不存在于 中的数据成员Base,并且您在Base对象(或派生自Base但不相关的对象Derived)上调用它,会发生什么情况?

反过来的转换是有道理的,这个没有。

于 2012-04-15T14:24:30.870 回答
3

不,这有潜在的危险。

派生类函数可以使用所有派生类属性*this。可以在任何基类实例上调用指向基类函数的指针,即使是那些不属于派生类型的实例。

访问不是派生类的实例的派生类属性是行不通的,因此不允许将派生类函数的指针转换为指向基类指针的指针。

另一方面,将指向基类函数的指针转换为指向派生类函数的指针是安全且合法的。

于 2012-04-15T14:26:00.763 回答
1

您需要使用std::function<void()>. 这可以是任何类的任何成员,一个 lambda,一个自由函数,一个函数对象,任何你需要的,超级方便。

#include <iostream>
#include <typeinfo>
using namespace std;
struct Base{
    std::function<void()> any_method;
    void call_it(){
        any_method();
    }
};
struct Derived: public Base{
    void a_method(){
        cout<<"method!"<<endl;
    }
};
int main(){
    Derived* d = new Derived;
    Base& a= *d;
    a.any_method = [d] { d->a_method(); };
    a.call_it();
}

在这里您可以看到 的实际实现any_method完全从 中抽象出来,我可以提供一个struct Base函数对象,它可以做任何事情,包括方便地调用 Derived 方法。

于 2012-04-15T14:28:36.233 回答
0

我想这可能有点令人惊讶。但是,如果您考虑一下,它是有道理的。

为了使两种类型之间的强制转换是自动的,以下关系应该成立:第一种类型的任何实例都应该可以在第二种类型中表示。

例如,如果d是 的一个实例Derived,那么它可以自动转换为 aBase&因为任何 的实例Derived也是 的一个实例Base。这是继承。

现在,当涉及到指向成员函数的指针时,这种关系实际上是相反的。很明显,任何方法都Base存在于 的实例中,Derived但反之则不成立。毕竟,推导的全部意义在于更频繁地添加新功能。

另一种可视化的方法是使用自由函数。this只是常规函数中的隐式参数,如果我们明确表示,我们会​​得到:

void Base@call_it(Base& self);

void Derived@a_method(Derived& self);

现在,如果我有两个dtypeDerivedbtype 的实例,Base那么:

  • Base@call_it(d)说得通
  • Derived@a_method(b)是编译错误

后者可能是Derived@a_method(dynamic_cast<Derived&>(b)),但这引入了运行时检查来实际验证属性。静态地它是不可判定的。

于 2012-04-15T14:54:22.773 回答