繁体   English   中英

为什么在C ++中使用基类指针无法到达派生类的成员函数? 它的设计思想是什么?

[英]Why cannot reach derived class's member function using a base class pointer in C++? What's the design idea of it?

我认为,使用基类指针可以达到派生类的内容。 也许我认为这就像火车一样,只要找到它的头,我就可以到达所有公共的地方。

但是,当我尝试执行此类操作时,出现一个错误,即基类无法找到派生类的成员函数。 尽管我使用基类指针变量来记录派生类的地址,但我认为它也可以达到训练头的派生类成员函数。 事实证明事实并非如此。 所以我想知道它的设计思想是什么。 为什么它的设计不像Python可以做到? 这种设计思想的优点是什么?

错误提示为打击: 在此处输入图片说明

示意图:

在此处输入图片说明

演示代码:

#include <iostream>

using namespace std;


class B{
public:
    void helloB(){
        cout << "hello B" << endl;
    }
};


class C:public B{
public:
    void helloC(){
        cout << "hello C" << endl;
    }
};

class A{
public:
    B* t;
    void helloA(){
        t->helloC();
    }
};

int main(){
    A a;
    C c;
    a.t = &c;
    a.helloA();
}

为什么?

C ++是静态类型的语言。 也就是说,它在编译时而不是在运行时进行类型检查。

在编译时,编译器无法知道t (它知道具有B*类型)具有helloC方法。 该语言试图通过调用未定义的行为(如果该方法不存在)来阻止您脚下弹。

相反,Python是一种动态语言,没有静态类型检查。

在静态检查与动态检查之间存在权衡 动态检查更灵活,但通常意味着您无法捕获某些错误,除非您在运行时使用所有代码路径。 静态检查有助于及早发现错误,并允许进行更好的优化(例如,更快)的代码。 例如,当调用A::helloA ,如果B::helloC存在,则程序无需首先检查t是否具有helloC方法; 在编译时就已经证明了这一点。

C ++是静态类型的,因为它的目标受众是想要优化速度的开发人员。

在C ++中,您可以做什么呢?

为了在C ++中避免这种情况,您必须将接口推入基类并使其成为虚拟方法,或者执行向下转换以强制将指向基类的指针(或引用)视为对派生类的指针/引用。 。 在这种情况下,您可以使用static_cast

void helloA(){
    static_cast<C*>(t)->helloC();
}

它告诉编译器您绝对可以肯定 t实际上是C* 这将不会增加运行时开销,但是也不会增加运行时开销,也就意味着没有运行时检查t是否实际上是C*

向下转换的一种更通用的方法是使用dynamic_cast

void helloA(){
    C* c = dynamic_cast<C*>(t);
    if (c != null) {
      c->helloC();
    }
}

这将在运行时检查t是否实际上是C* 当然,运行时检查会产生运行时成本。

好的,编译器除了是类B的实例外,对有关对象一无所知。您可以从B派生一些其他类,并且它们将适合于类B可以适合的任何地方。 考虑一下:B类是更一般的东西(比方说是动物),而C类或从B派生的任何其他类是某种动物。 假设是狗或鸟。 假设所有动物都可以发出声音,但只有鸟类可以产卵。 现在您可以拥有一组动物,并告诉它们全部发出声音,但是您不能告诉所有它们都下蛋。 在此抽象级别上,编译器没有下蛋的想法。 在您的代码中,编译器无法确定您是在谈论类C的实例。

我的朋友,您需要阅读一些有关c ++继承的知识,或者至少要了解c ++和python之间的继承差异。

从您的示例中,您似乎在问狗说喵喵:D

C类是从B类继承而来的,因此不能从B类的对象调用C类的helloC方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM