• 【笔试题】【day15】


    目录

    第一题(多态)

    第二题(多态-函数默认值是父类还是子类的问题) 

    第三题(纯虚函数的声明)

    第四题(多态)

    第五题(父类的析构函数最好加上virtual!)


    第一题(多态)

    有如下C++代码:

    1. struct A{
    2. void foo(){printf("foo");}
    3. virtual void bar(){printf("bar");}
    4. A(){bar();}
    5. };
    6. struct B:A{
    7. void foo(){printf("b_foo");}
    8. void bar(){printf("b_bar");}
    9. };
    1. A *p = new B;
    2. p->foo();
    3. p->bar();

    A barfoob_bar
    B foobarb_bar
    C barfoob_foo
    D foobarb_fpp

    这里按照我们运行的逻辑,首先我们new B也就是构造了一个B对象,然后B由于是继承A类的,所以B在构造的时候会调用A的构造函数。

    在A的构造函数中有

    所以会调用bar()函数,这里是A类的对象调用bar函数,所以不满足多态的条件,所以直接调用A类的bar函数,打印bar

    然后由于我们的p是父类A的指针,然后指向的是B类(子类),然后调用foo()方法的时候,由于我们的父类方法的foo前面并没有写virtual,所以并不满足多态的调用条件,所以A类的指针调用A类的方法,也就是打印出foo

    然后p->bar()的时候,我们父类A中的bar方法前面有virtual,所以子类的bar()函数前面不写virtual,依然也构成多态,所以这里调用子类的bar方法,也就是打印出b_bar

    所以一共打印了barfoob_bar,也就是A

    A

    第二题(多态-函数默认值是父类还是子类的问题) 

    以下程序输出结果是()

    1. class A
    2. {
    3. public:
    4. virtual void func(int val = 1)
    5. { std::cout<<"A->"<
    6. virtual void test()
    7. { func();}
    8. };
    9. class B : public A
    10. {
    11. public:
    12. void func(int val=0) {std::cout<<"B->"<
    13. };
    14. int main(int argc ,char* argv[])
    15. {
    16. B*p = new B;
    17. p->test();
    18. return 0;
    19. }

    A A->0
    B B->1
    C A->1
    D B->0

    这里我们先初始化了一个B类型的对象,然后用一个B类型的指针指向我们刚刚创建的B类型对象,然后用我们的B类型并且指向B类型对象的指针调用test方法,这里是不满足多态的调用条件的!

    但是我们这里的B类中并没有test函数,但是A类中有,并且我们的B类是继承于A类的,所以我们会调用A类中的test函数。

    我们的A类中的test函数又调用了func函数,这时,由于我们是A类,所以我们的this指针是A类的,然后我们的当前指针的指向是一个B类的函数,并且这里的func前有virtual,并且子类中也重写了func(),所以是满足多态调用的条件的,所以我们这里会调用子类的func,也就是打印B->

    这里的默认值在程序编译时期就已经确定了,而我们的多态是动态调用的,所以没有办法动态更改这个默认值

    所以我们的默认值依旧是A类中的func的val,也就是1

    所以打印B->1

    第三题(纯虚函数的声明)

    下列为纯虚函数的正确声明的是()

    A void virtual print()=0;
    B virtual void print()=0;
    C virtual void print(){};
    D virtual void print()\;

    这里其实A,B都是可以的,但是在习惯上我们写B的写法

    C中加了{},已经不算是声明了,还有定义也同时定义了

    D本身就是错的,没有这样的写法

    B

    第四题(多态)

    下面这段代码运行时会出现什么问题?

    1. class A
    2. {
    3. public:
    4. void f()
    5. {
    6. printf("A\n");
    7. }
    8. };
    9. class B: public A
    10. {
    11. public:
    12. virtual void f()
    13. {
    14. printf("B\n");
    15. }
    16. };
    17. int main()
    18. {
    19. A *a = new B;
    20. a->f();
    21. delete a;
    22. return 0;
    23. }

    A 没有问题,输出B
    B 不符合预期的输出A
    C 程序不正确
    D 以上答案都不正确

    这里我们子类的构造出了一个对象

    这里我们的父类的A指针指向了子类的B对象,然后我们的父类中没有virtual,所以不构成多态。

    由于我们的指针是一个父类的指针,所以调用父类的f方法,所以我们打印出A

    但是这个程序在空间释放的时候存在问题!

    我们怎么产生虚表?

    首先我们构造子类的时候,我们会先构造一个父类,但是此时我们的父类没有虚函数,所以它不会有所谓的虚表,只有构造到子类的时候,才会有虚表。

    但是虚表的指针往往放在它整个对象的第一个成员,也就是(__vfptr) 

    但是这个指针又往往会存活于父类部分。

    由于这根时候父类没有虚函数,它也就没有办法安插这样的一个虚表指针,但是子类在释放的时候,又需要将它的空间释放掉,这就会导致我们想要释放一些空间,但是父类不提供这样的一些指针,就会导致空间释放的失败。更有可能导致空间的非法访问。

    这里我们只要给父类增加一个虚函数,这里的使用就不会有任何问题。因为只要有了虚函数,我们的父类中就会产生虚表,就会给我们提供给子类去用,然后再释放空间的时候就会进行检查。

    比方说这样

    1. class A
    2. {
    3. public:
    4. void f()
    5. {
    6. printf("A\n");
    7. }
    8. virtual void g()
    9. {
    10. }
    11. };
    12. class B: public A
    13. {
    14. public:
    15. virtual void f()
    16. {
    17. printf("B\n");
    18. }
    19. };
    20. int main()
    21. {
    22. A *a = new B;
    23. a->f();
    24. delete a;
    25. return 0;
    26. }

    第五题(父类的析构函数最好加上virtual!)

    下面这段代码会打印出什么?

    1. class A
    2. {
    3. public:
    4. A()
    5. {
    6. printf("A ");
    7. }
    8. ~A()
    9. {
    10. printf("deA ");
    11. }
    12. };
    13. class B
    14. {
    15. public:
    16. B()
    17. {
    18. printf("B ");
    19. }
    20. ~B()
    21. {
    22. printf("deB ");
    23. }
    24. };
    25. class C: public A, public B
    26. {
    27. public:
    28. C()
    29. {
    30. printf("C ");
    31. }
    32. ~C()
    33. {
    34. printf("deC ");
    35. }
    36. };
    37. int main()
    38. {
    39. A *a = new C();
    40. delete a;
    41. return 0;
    42. }

    A A B C deA
    B C A B deA
    C A B C deC
    D C A B deC

    这里我们构造了一个C的对象,然后C再继承的时候,顺序为A然后再是B 

     

    所以先打印A,然后是B,最后是C

    然后由于我们的指针的类型是A类型的,所以我们在使用我们的a指针去调用析构函数的时候,

    由于我们的A类也就是我们的父类的析构函数并没有加上virtual,所以并不构成多态

    所以A类型的指针只能够调用A的析构函数,所以打印出deA

    但是我们的B和C并没有正确地被析构!

    A

  • 相关阅读:
    一个练习 k8s 的仓库,通过动手的方式掌握k8s
    DT Paint Effects工具(一)
    MySQL面试题
    十天学完基础数据结构-第七天(图(Graph))
    GoLang Map 实现分析
    22.2 正则表达式-数据验证、数据变换
    Vue3.2开发一个博客Demo项目
    K8s如何从私有Harbor中拉取镜像并完成部署
    Vue理解之路:如何在Vue项目中使用vuex
    ML:置信区间的简介(精密度/准确度/精确度的三者区别及其关系)、使用方法、案例应用之详细攻略
  • 原文地址:https://blog.csdn.net/weixin_62684026/article/details/127559683