目录

题目 

代码题

分析

主观题


题目 

代码题

class A
{
public:
    virtual void func(int val = 1) {
        std::cout << "A->" << val << std::endl;
    }
    virtual void test() { 
        func(); 
    }
};

class B : public A
{
public:
    void func(int val = 0) { 
        std::cout << "B->" << val << std::endl;
    }
};

int main(int argc, char* argv[])
{
    B* p = new B;
    p->test();
    return 0;
}

A: A->0       B: B->1      C: A->1       D: B->0       E: 编译出错       F: 以上都不正确

分析

p调用了B中的test(),传入B的this指针

  • 但test()在B中没有,它是A中的函数,且没有被重写,所以需要去A中寻找
  • 而调用A中函数需要使用A类型的this指针,所以B类型的this指针被转换为A类型的
  • 而调用了test()后,里面又会调用func()
  • 别忘了,这里的this指针实际还是指向B的(只是指在了B中的A部分)
  • 所以,根据多态性,调用了B中的func,所以要打印B->
  • 注意这里的val
  • 多态实际上是使用了基类中函数的声明+子类中该函数的定义
  • 所以!!!val的值是A中的1(int val = 1)
  • 所以最后结果是B->1

 

主观题

1. 什么是多态?

多态 -- 完成同一个动作,根据指针/引用实际指向的对象类型,来调用函数

2. 什么是重载、重写(覆盖)、重定义(隐藏)?

  • 重载 -- 在同一作用域下,两个函数的名字相同,但参数列表/返回值不同
  • 重定义/隐藏 -- 在继承关系中,父类和子类都有一个同名函数,无论参数如何,都会构成隐藏,优先调用子类的函数
  • 重写/覆盖 -- 在继承关系中,父类和子类都有一个相同的虚函数(同名,同参数,同返回值),就说子类的该函数重写了父类的该函数 ; 当使用父类的指针/引用调用该函数时,会实际调用子类中的

3. 多态的实现原理?

虚函数+虚函数表+运行时多态性

  • 虚函数 -- 可以在派生类中被重写
  • 虚表 -- 存储类中虚函数的指针
  • 运行时多态性 -- 实现运行时可以根据实际对象调用对应函数

4. inline函数可以是虚函数吗?

可以

  • 但内联函数原本是不生成地址的,直接在调用处展开代码
  • 而虚函数需要将地址存入虚表
  • 所以一旦成为虚函数,就会失去内联的特性

5. 静态成员可以是虚函数吗?

不可以

  • 静态成员属于整个类,调用时直接使用类域即可
  • 虚函数调用需要传入this指针(需要this指针来找到该对象的虚表,在里面寻找虚函数),而静态成员没有this指针

6. 构造函数可以是虚函数吗?

不可以

  • 因为构造函数是用来初始化对象的,包括设置对象的虚表指针,而虚函数的调用依赖于虚表指针
  • 所以虚表和虚函数的机制依赖于已经正确初始化的对象 -- 构造函数->虚表指针->虚函数
  • 如果构造函数是虚函数,那么调用的时候需要依赖虚表找到虚函数,但虚表又是在构造函数之后才能创建好
  • 那么这个先后顺序就很迷惑了,所以不可以是虚函数

7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?

当然可以

  • 编译器会对析构函数的函数名做处理,从而达到虚函数的条件
  • 场景 -- 当需要使用基类类型的指针/引用指向派生类,而没有其他方法析构该派生类时(防止内存泄漏)

8. 对象访问普通函数快还是虚函数更快?

普通函数更快

  • 因为虚函数需要在运行时,根据类型 -> 去对象中找虚表 ->在虚表中找虚函数
  • 而普通函数直接jmp到函数地址即可

9. 虚函数表是在什么阶段生成的,存在哪的?

  • 编译阶段生成
  • 存在常量区

10. C++菱形继承的问题?虚继承的原理?

一个派生类对象中,会存储两份共同基类,造成数据冗余+二义性问题

虚继承 :

  • 将基类声明成虚基类,该类在派生类中只存在一份
  • 会在包含它的派生类中,存放一份虚基表,使可以通过该表中存放的偏移量来访问基类

11. 什么是抽象类?抽象类的作用?

  • 抽象类 -- 包含纯虚函数的类(纯虚函数 -- 在基类中的虚函数后加上=0)
  • 作用 -- 定义一个通用的接口,然后由具体的派生类来实现这个接口以提供不同的行为

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部