通过基类指针取消引用

De-referencing through base class pointer

本文关键字:取消 引用 指针 基类      更新时间:2023-10-16

最近关于SO的一个问题让我思考了以下问题
考虑以下代码:

class Base{
public:
void print() { cout<<"In Base"<<endl;}
};
class Derived: public Base{
public:
void print() { cout<<"In Derived"<<endl;}
};
int main(void)
{
Base *bp;
Derived ob;
bp = &ob;
(*bp).print();  // prints: In Base
ob.print();     // print: In Derived 
return 0;
}

为什么(*bp),print()的行为与ob.print()不同。

我认为(*bp)应该返回对象ob,因为bp被引用到对象ob,并且当我们使用*运算符取消引用时,我们得到该地址的值,并且ob对象存储在bp中存在的地址。因此,第一个函数调用应该与发送函数调用相同。

请澄清概念。

你所做的被称为"方法隐藏",是一种代码气味,一种坏习惯,一种不应该做的事情。

您应该在基类中将打印方法定义为virtual。(也不要忘记Base类中的virtual析构函数。)

class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
virtual ~Base(){}
};
class Derived: public Base{
public:
void print() { cout<<"In Derived"<<endl;}
};

您是否错过了声明函数print()virtual?;)

class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
};

决策始终基于指针类型而非指针内容。前者的绑定被称为Early Binding,后者是Late Binding

如果我们重新定义前面有virtual关键字的方法print(),结果将是必需的。

class Base
{
public:
virtual void print() 
{ 
cout<<"In Base"<<endl;
}
};
class Derived: public Base
{
public:
void print() 
{ 
cout<<"In Derived"<<endl;
}
};

当您使用指向基类的指针或引用引用派生类对象时,可以为该对象调用虚拟函数,并执行派生类的函数版本。虚拟函数确保为对象调用正确的函数,而不管用于调用函数的表达式是什么。

当使用指针或引用调用函数时,以下规则适用:

  • 对虚拟函数的调用根据底层为其调用的对象的类型
  • 对非虚拟函数的调用根据指针或引用

您可以浏览虚拟表和虚拟函数的概念以了解更多详细信息

编译器使用静态类型的变量。变量bp具有静态类型Base*,因此编译器会检查Base的类定义中是否存在这样的函数并调用它。您想要达到的效果可以使用虚拟函数来实现。在这种情况下,编译器使用指向虚拟函数的指针表来调用函数。在这种情况下,由于bp具有动态类型Derived*,因此表将包含指向Derived类中定义的虚拟函数的指针。您所需要做的就是将函数说明符virtual添加到Base类中的函数定义中

class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
virtual ~Base() = default};

Base可能有很多派生类。在(*bp).print()的时刻,您不知道它们中的哪一个(如果有的话)是底层对象的真实类。所以编译器所能做的就是调用Base::print

但是,如果您包含了一些关于实际类(vtable)的信息,那么就可以按照您想要的方式进行操作(按照其他人的建议,使用virtual函数)。