2012-04-15 86 views
4

对我来说,它看起来很安全在此代码投下void(Derived::*)()void(Base::*)(),如:为什么我不能将指向派生类成员函数的指针转换为相同的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模板)。

回答

6

会发生什么,如果你的Derived::a_method()尝试使用仅出现在Derived,而不是在Base数据成员,你怎么称呼它一个Base对象(或Base衍生而来,但没有涉及到Derived对象)吗?

转换其他方式是有道理的,这不是。

3

不,它有潜在危险。

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

访问未派生类是不会工作,所以铸件的指针派生类函数的指针基类指针实例的派生类的属性是否正确不允许的。

在另一方面,铸造一个指向基类的函数的指针派生类的功能是安全和合法的。

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,我可以在清一色提供一个函数对象,做任何事情,包括方便地调用了派生方法。

+1

std :: function可以在这里帮助:'std :: function '有同样的问题,如果放弃给any_method成员的类型函数,那么没有必要使用'std :: function'而不是普通的空闲函数指针。对? – 2012-04-15 14:40:08

+0

@洛伦佐:号'std :: function '。该函数可以存储它自己的'this'指针。另外,例如,在函数指针 - 有状态lambda表达式上总是有'std :: function'指向'std :: bind'的结果。我编辑了我的答案,以使std :: function'能够立即解决这个问题。 – Puppy 2012-04-15 16:42:29

0

我想这可能有点令人惊讶。不过,如果你仔细想想它是有道理的。

对于两种类型之间的流延是自动的,下面的关系应持有:所述第一类型的任何实例应该在第二表示的。

例如,如果dDerived一个实例,则它可以自动地转换为Base&由于Derived任何实例也是Base一个实例。这是继承。

现在,当涉及到指针成员函数,关系实际上是颠倒。看起来很明显,对于Derived的实例存在Base的任何方法,但反过来是不正确的。毕竟所有的推导都是为了更经常地添加新的功能。

可视化的另一种方法是使用自由函数。 this是在常规功能只是一个隐含参数,如果我们把它明确我们得到:

void [email protected]_it(Base& self); 

void [email protected]_method(Derived& self); 

现在,如果我有两个实例DeriveddBase类型则b

后者可能是[email protected]_method(dynamic_cast<Derived&>(b)),但是这会引入运行时检查来实际验证属性。静态它是不可判定的。

相关问题