文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++多态如何使用

2023-07-02 10:59

关注

本文小编为大家详细介绍“C++多态如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++多态如何使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

多态

多态离不开继承,首先来定义一个基类 Animal,里面有一个虚函数 speak()

class Animal {public:    Animal() = default;    Animal(string name) : m_name(name) {}    virtual ~Animal() = default;    virtual void speak() const { cout << "Animal speak" << endl; }    string name() const { return m_name; }private:    string m_name;};

接着定义子类 Dog,并重写虚函数,由于构造函数无法继承,所以使用 using 来 “继承” 父类的构造函数。和父类相比,Dog 还多了一个 bark() 方法。

class Dog : public Animal {public:    using Animal::Animal;    // 可加上 override 声明要重写虚函数,函数签名必须和基类相同(除非返回类自身的指针或引用)    void speak() const override { cout << "Dog bark" << endl; }    void bark() const { cout << "lololo" << endl; }};

向上转型

我们在堆上创建一个 Dog 对象,并将地址赋给一个 Animal 类型的指针。由于指针指向的是个 Dog 对象,调用 speak() 方法时,实际上调用的是底层狗狗重写之后的 speak() 方法,而不是基类 Animal 的 speak()。也就是说编译时不会直接确定要调用的是哪个 speak() ,要在运行时绑定。

Animal* pa = new Dog("二哈");pa->speak(); // 调用的是 Dog::speakpa->Animal::speak(); // 强制调用基类的 speak

利用运行时绑定这一特点,我们将基类的析构函数定义为虚函数,这样子类对象在析构的时候就能调用自己的虚函数了。

虽然 pa 指向的是一个 Dog 对象,但是不能使用 bark() 方法。因为 pa 是一个 Animal 类型的指针,在编译时编译器会跳过 Dog 而直接在 Animal 的作用域中寻找 bark 成员,结果发现并不存在此成员而报错。

要实现向上转型不止能用指针,引用同样可以实现。但是如果写成以下这种形式,实质上是调用了拷贝构造函数,会用 Dog 的基类部分来初始化 Animal 对象,和向上转型没有任何关系,之后调用的就是底层 Animal 对象的 speak() 方法:

Dog dog("二哈");Animal animal = dog;animal.speak(); // 调用的是 Animal::speak

向下转型

要想调用底层 Dog 对象的 bark() 方法,我们需要将 pa 强转为 Dog 类型的指针。一种方法是使用 static_cast 进行静态转换,另一种这是使用 dynamic_cast 进行运行时转换。相比于前者,dynamic_cast<type *> 转换失败的时候会返回空指针,而 dynamic_cast<type &> 则会报 bad_cast 错误,因此更加安全。

Dog* pd_ = static_cast<Dog *>(pa);pd_->bark();if (Dog* pd = dynamic_cast<Dog*>(pa)) {    pd->bark();} else {    cout << "转换失败" << endl;}

作用域

子类的作用域是嵌套在父类里面的,在子类的对象上查找一个成员时,会现在子类中查找,如果没找到才回去父类中寻找。由于作用域的嵌套,会导致子类隐藏掉父类中的同名成员。比如下述代码:

class Animal {public:    virtual void speak() const    {        cout << "Animal speak" << endl;    }};class Dog : public Animal {public:    // void speak() const override { cout << "Dog speak" << endl; }    void speak(string word) const    {        cout << "Dog bark: " + word << endl;    }};int main(int argc, char const* argv[]){    Animal* pa = new Dog();    Dog* pd = new Dog();    // pd->speak(); 报错    pd->speak("666"); // Dog::speak 隐藏了 Animal::speak    return 0;}

我们在父类中定义了一个虚函数 void speak(),子类中没有重写它,而是定义了另一个同名但是参数不同的函数 void speak(string word)。这时候子类中的同名函数会隐藏掉父类的虚函数,如果写成 pd->speak(),编译器会先在子类作用域中寻找名字为 speak 的成员,由于存在 speak(string word),它就不会接着去父类中寻找了,接着进行类型检查,发现参数列表对不上,会直接报错。如果用了 VSCode 的 C/C++ 插件,可以看到参数列表确实只有一个,没有提示有重载的同名函数。

C++多态如何使用

要想通过调用基类的 speak() 方法,有两种方法:

《C++ Primer》对名字查找做了一个非常好的总结:

理解函数调用的解析过程对于理解 C++ 的继承至关重要,假定我们调用 p->mem() (或者 obj.mem()),则依次执行以下4个步骤:

首先确定 p (或 obj) 的静态类型。因为我们调用的是一个成员,所以该类型必 然是类类型。

在 p (或 obj ) 的静态类型对应的类中查找 mem。如果找不到,则依次在直接基类中不断查找直至到达继承链的顶端。如果找遍了该类及其基类仍然找不到,则编译器将报错。

一旦找到了 mem,就进行常规的类型检查以确认对于当前找到的 mem,本次调用是否合法。

假设调用合法,则编译器将根据调用的是否是虚函数而产生不同的代码:

读到这里,这篇“C++多态如何使用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯