【C++】早绑定、析构与多态 | 一道关于多态的选择题记录

这篇具有很好参考价值的文章主要介绍了【C++】早绑定、析构与多态 | 一道关于多态的选择题记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

今天在和群友聊天的时候看到了一道很坑的题目,分享给大家

1.看题!

先来看看题目

struct Dad
{
public:
    Dad(){ echo();}
    ~Dad(){ echo();}
    virtual void echo() {
        cout << "DAD ";
    }
};

struct Son:Dad
{
public:
    void echo() const override {
        cout << "SON ";
    }
};

Son ss;

请问这个的输出是什么?

A  "DAD DAD "
B  "DAD SON "
C  "SON DAD "
D  "SON SON "
E  编译出错
F  运行出错

答案是E,编译出错!

2.涉及到的知识点

2.1 知识点

先来说说这道题目里面涉及到了什么知识点

  • 多态调用;
  • 多态重写函数需要满足什么条件;
  • 类内函数后加const的作用;
  • 类内函数后加override的作用;
  • 什么是早绑定和晚绑定

一个一个复习吧!

  • 多态调用是父类指针/引用指向子类时,调用虚函数会调用子类重写后的版本
  • 多态重写函数的条件:函数名/参数/返回值都必须相同(注意还有协变)
  • 类内函数后加const修饰的是这个对象的this指针,被修饰的函数中无法修改类内成员变量
  • 类内函数后加override是让编译器来严格检查是否构成重载
  • 早绑定:静态绑定;晚绑定:动态绑定(具体请看CPP多态的博客)

2.2 分析题目

注意看父类和子类中这两个echo()函数的区别

virtual void echo(){}//父类
void echo() const override {}//子类

首先需要说明的是,子类函数中virtual关键字是可以省略的,但即便省略了,这个函数依旧是个虚函数。

这里子类的函数中多了const修饰,而这个const修饰的就是函数中隐含的this指针,此时子类中echo()函数的参数就发生了变化!

virtual void echo(Son* this) { } // 不加const
virtual void echo(const Son* this) { } // 加const

正是因为这里的this指针出现了const的修饰,所以子类的echo和父类echo的参数类型不同,不构成虚函数重写!再加上override关键字的严格检查,会直接编译报错!

正确的写法是删除子类echo中的const或者给父类echo函数加上const

3.再来看题

好了,坑人的点看完了,再来看个「常规」的,就是把上面的题干改成能编译通过的。此时又应该选谁呢?

struct Dad
{
public:
    Dad(){ echo();}
    ~Dad(){ echo();}
    virtual void echo() const{
        cout << "DAD ";
    }
};

struct Son:Dad
{
public:
    void echo() const override {
        cout << "SON ";
    }
};

Son ss;
A  "DAD DAD "
B  "DAD SON "
C  "SON DAD "
D  "SON SON "

【C++】早绑定、析构与多态 | 一道关于多态的选择题记录,遇见C++,c++,开发语言

编译运行,可以看到,结果是DAD DAD,应该选A

3.1 分析

在给 Son 类定义构造函数和析构函数时,没有指定调用父类的对应构造函数和析构函数。因此,在创建 Son 对象 ss 时,会默认调用 Dad 类的构造函数和析构函数。

由于 Dad 类中的构造函数和析构函数调用了虚函数 echo(),而这个虚函数在子类 Son 中被重写,所以会根据对象类型调用相应的重写函数。然而,在构造函数和析构函数中,虚函数机制不会按照预期工作。

构造函数中调用虚函数时,会忽略动态绑定机制,直接调用父类的函数版本。因此,在 Dad 的构造函数中调用 echo(),实际上调用的是 Dad 类中的 echo() 函数,而不是 Son 类中的重写版本。

同样地,析构函数中也会忽略动态绑定机制,直接调用父类的函数版本。所以,在 Dad 的析构函数中调用 echo(),依然调用的是 Dad 类中的 echo() 函数。

因此,当创建 Son 对象 ss 并打印输出时,会先调用 Dad 类的构造函数并打印 "DAD ",然后调用 Dad 类的析构函数并再次打印 "DAD "

3.2 结论

在父类的构造和析构中,对象的版本都被确定为父类的版本,会采用早绑定来调用父类自己的函数,而不是子类的重写后的函数;

简单记忆:父类的构造和析构中如果出现虚函数,只会调用父类自己的函数!


这是因为编译器需要保证正确的构造和析构顺序,如果父类析构里调用子类的虚函数,可能会出现下面的场景

struct Dad
{
public:
    Dad(){ echo();}
    ~Dad(){ echo();}
    virtual void echo() const{
        cout << "DAD ";
    }
};

struct Son:Dad
{
public:
    Son() {
        _a = new int(3);
    }
    ~Son() {
        delete _a;
    }
    void echo() const override {
        cout << "SON ";
        delete _a;
    }
private:
    int _a;
};

Son ss;

如果父类中的析构echo()调用子类重写的函数,此时就会出现子类已经被销毁(子类的析构函数早于父类析构调用)的_a被二次delete,两次delete同一片空间是会报错的!

所以为了避免这种情况,父类的析构中采用早绑定,子类重写的虚函数不会生效!

这种行为是为了确保在对象的构造和析构过程中,按照正确的顺序调用各个类的构造和析构函数,避免在对象处于未完全初始化或已部分销毁状态时调用子类的函数。

包括父类的构造也可以这么理解,如果父类构造里面可以调用子类的虚函数,可能会出现两次对一个子类对象进行new空间,会产生内存泄露;

但构造函数还和虚函数表的初始化有关系,此时虚函数表还没有完全初始化,子类对象尚未构造完成,没有多态调用的条件,所以也不能调用到子类重写后的虚函数。文章来源地址https://www.toymoban.com/news/detail-668772.html

到了这里,关于【C++】早绑定、析构与多态 | 一道关于多态的选择题记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c++选择题笔记

    c++的三大特性:封装,多态,继承 局部变量能否和全局变量重名?可以,局部变量会屏蔽全局变量。在使用全局变量时需要使用 \\\":: \\\"。 拷贝构造函数:参数为同类型的对象的常量引用的构造函数 函数指针:int (*f)(int,int) = max;  静态成员函数 没有this指针。 静态成员不能是虚

    2024年02月12日
    浏览(38)
  • C++期末考试选择题题库100道

    1. 对于常数据成员,下面描述正确的是 【 B 】 A. 常数据成员必须被初始化,并且不能被修改 B. 常数据成员可以不初始化,并且不能被修改 C. 常数据成员可以不初始化,并且可以被修改 D. 常数据成员必须被初始化,并且可以被修改 2. 在C++中,使用流进行输入输出,其中用于

    2023年04月17日
    浏览(49)
  • [C++] 多态(下) -- 多态原理 -- 动静态绑定

    上一篇文章我们了解了虚函数表,虚函数表指针,本篇文章我们来了解多态的底层原理,更好的理解多态的机制。 [C++] 多态(上) – 抽象类、虚函数、虚函数表 下面这段代码中,Func函数传Person调用的Person::BuyTicket,传Student调用的是Student::BuyTicket,这就是多态调用,但是这里我

    2024年02月04日
    浏览(49)
  • 【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

    梳理虚函数表、多态原理、动静态绑定的知识 目录 一、虚函数表 二、多态的原理 三、动态绑定和静态绑定 在学习多态原理之前,我们需要了解一下虚函数表的概念  我们先一起来看下下面这段代码 通过测试我们发现b对象是8bytes, 除了_b成员,还多一个__vfptr指针放在对象

    2024年02月03日
    浏览(42)
  • C++期末考试选择题题库100道&&C++期末判断题的易错知识点复习

    今天备考C++,看到了一些好的复习资料,整合一起给大家分享一下 对于常数据成员,下面描述正确的是 【 B 】 A. 常数据成员必须被初始化,并且不能被修改 B. 常数据成员可以不初始化,并且不能被修改 C. 常数据成员可以不初始化,并且可以被修改 D. 常数据成员必须被初始

    2024年02月10日
    浏览(59)
  • 【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】

    前言 大家好吖,欢迎来到 YY 滴C++考前速过系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《

    2024年01月22日
    浏览(62)
  • hive基础选择题

    1.下列关于Hive特点总结正确的选项是() A Hive支持自由的扩展集群的规模,只需要重启服务即可 B Hive支持自定义函数,用户可以根据自己的需求去定义函数 C Hive SQL执行时,需要避免节点出现问题 D Hive 适合处理小批量数据 2.下面关于Hive查看表table1的分区的描述正确的是()

    2023年04月26日
    浏览(56)
  • day45—选择题

    A 205 B 205.140 C 68 D 205.140.36 思路:A类地址网络号是0 ~ 127,B类地址网络号是128.0 ~ 191.255,C类是192.0.0 ~ 223.255.255,因此这是一个C类网络,24位网络号 A 服务端收到客户端的SYN包后等待2 ml时间后就会进入SYN_SENT状态 B 服务端收到客户端的ACK包后会进入SYN_RCVD状态 C 当客户端处于ESTA

    2024年02月04日
    浏览(36)
  • 云计算选择题(二)

    云计算知识点-CSDN博客 云计算选择题及答案-CSDN博客 云计算试题及答案-CSDN博客 云计算简答题-CSDN博客 云计算简答题(二)-CSDN博客 云计算单选题及答案-CSDN博客 云计算多选题及答案-CSDN博客 云计算选择题(二)-CSDN博客  与SaaS不同的,这种“云”计算形式把开发环境或者

    2024年04月22日
    浏览(32)
  • day31—选择题

    A 1 B 9 C 10 D 11 思路:CPU中只能处理一个,一共有12个进程,那么处在就绪队列中的最多就是11个 A 线程同步的方法包括使用临界区,互斥量,信号量等 B 两个线程同时对简单类型全局变量进行写操作也需要互斥 C 实现可重入函数时,对自动变量也要用互斥量加以保护 D 可重入函

    2023年04月20日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包