无法从继承的类分配指向方法的指针

Can't assign a pointer to method from an inherited class

本文关键字:方法 指针 分配 继承      更新时间:2023-10-16

我有两个类:

class   IBaseA
{
public:
  virtual bool  sayHello(std::string&) = 0;
  ~IBaseA(){}
};                                                                                                   
class   BaseA: public IBaseA
{
public:
  virtual bool  sayHello(std::string &str)
  {
    std::cout << "Hello " << str << std::endl;
    return (true);
  }
  BaseA(){}
};

你能解释一下为什么我不能那样做吗

bool  (IBaseA::*ptr)(std::string&) = &BaseA::sayHello;

错误:无法转换'bool (BaseA::)(std::string&){也称为bool(BaseA::)(std::basic_string&)}' to 'bool(IBaseA::)(std::string&){也称为bool(IBaseA:: ) (std:: basic_string&)}"初始化

我不明白为什么我不能这样做。

但是如果我把赋值改为

bool  (IBaseA::*ptr)(std::string&) = &IBaseA::sayHello;

那么我可以毫无问题地使用这个指针

BaseA A;
(A.*ptr)(str);

编辑:谢谢你的回答,我在想,因为所有的地址都在虚表中,根据我从维基百科所理解的,它们是相同的位置。

指针到成员的转换与一般的指针转换相反。可以将指向派生类的指针转换为指向基类的指针,但反之则不行。可以将基类的指针指向成员转换为派生类的指针指向成员,但反之则不行。

始终考虑转换是否有效。

普通指针:所有BaseA的实例都是IBaseA的实例,因此转换是合法的。并不是所有的IBaseA实例都是BaseA实例,所以你不能以其他方式转换。

成员指针:IBaseA的所有成员都存在于BaseA中,因此可以将IBaseA的成员指针转换为BaseA的成员指针。然而,BaseA的所有成员并不是都存在于IBaseA中,因此不能反过来进行转换。

给定两个指向成员的指针类型T C1::*P1T C2::*P2(其中T可能是函数类型),如果C2派生自C1,则只能将P1转换为P2。注意,这是指向对象的指针的逆操作,对象指针只能向上转换。

原因是,如果有一个指向基类成员的指针,则任何子类都保证也具有该成员,因为它继承了该成员。反之则不适用。

这是基本的类型转换规则。它不会因为你取的指针恰好是基类中存在的虚函数的重写而改变

真正的答案可能是"因为语言规范说你不能",然而,想想你在尝试做什么:

bool  (IBaseA::*ptr)(std::string&) = &BaseA::sayHello;

本质上,这读作:我想要一个名为ptr的变量,它是一个指向IBaseA成员函数的指针,我想让它指向BaseA中的方法'sayHello'。

是的,BaseA中的sayHello方法覆盖了IBaseA中的sayHello方法,但它们仍然是不同的方法。想象一下,如果您尝试使用指针来调用类型为IBaseA的对象上的方法,而实际上不是 BaseA—这在语法上是允许的,但是您会期望发生什么?

换句话说,将指向派生类成员的指针赋值给声明为指向基类成员的变量,将破坏静态类型安全。

虽然这看起来有点违反直觉,但是不能将派生类型的指向成员的指针转换为指向基类型的指向成员的指针。正确的转换是另一种方式:您可以将指向基的成员指针转换为指向派生的成员指针。

原因是,如果基类具有该成员,则派生类型保证具有该成员,但反之则不为真(即,指向派生类型的成员的指针可以指向已添加且不存在于基类中的成员)。

你可能错过的另一件事是,指针到成员函数是多态的,也就是说,你可以存储&IBase::sayHello,如果它应用的成员类型是Base,它将调用Base::sayHello