首页 文章详情

C++ 面试考点(四)

字节流动 | 242 2022-07-16 15:04 0 0 0
UniSMS (合一短信)

31、抽象基类为什么不能创建对象?

  • 抽象类的定义:带有纯虚函数的类为抽象类。

  • 抽象类的作用:

    • 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。

    • 所以抽象类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些语义,也可以再将这些语义传给自己的子类。

  • 使用抽象类时注意:

    • 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。

    • 如果派生类中没有给出所有纯虚函数的实现,而只是继承基类的纯虚函数,则这个派生类仍然是一个抽象类

    • 如果派生类中给出了所有纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。

    • 抽象类是不能定义对象的


  • 纯虚函数定义: 纯虚函数是一种特殊的虚函数,它的一般格式如下:

      class <类名>
      {
          virtual <类型><函数名>(<参数表>)=0;
          …
      };
  • 纯虚函数引入原因

    • 为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

    • 在很多情况下,基类本身生成对象是不合情理的。

    • 例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

  • 为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法: virtual ReturnType Function()= 0;)。

    • 若要使派生类为非抽象类,则编译器要求在派生类中,必须对纯虚函数予以重载以实现多态性。

    • 同时含有纯虚函数的类称为抽象类,它不能生成对象。


  • 相似概念

    • 包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。

    • 虚函数是在基类中被声明为`virtual`,并在派生类中重新定义的成员函数,可实现成员函数的动态重载。

    • 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。

    • C++支持两种多态性:编译时多态性,运行时多态性。

    • 编译时多态性(静态多态):通过重载函数实现。

    • 运行时多态性(动态多态):通过虚函数实现。

    • 多态性

    • 虚函数

    • 抽象类

32、类什么时候会析构?

  • 对于静态对象: 当离开作用区域之后, 对象生命周期结束, 编译器会自动调用析构函数

  • 对于动态对象: 当对对象指针调用delete时, 会调用析构函数终止对象生命周期并释放内存. 其中对象指针指针可以对象类型的指针, 也可以时基类指针(注意基类析构函数位虚函数)

  • 第三种情况: 当对象中存在嵌入对象时, 该对象析构时, 嵌入对象也会被析构

33、为什么友元函数必须在类内部声明?

  • 因为编译器必须能够读取这个结构的声明以理解这个数据类型的大、行为等方面的所有规则。

  • 有一条规则在任何关系中都很重要,那就是谁可以访问我的私有部分。


  • 编译器通过读取类的声明从而进行类的访问权限控制, 而友元函数有权访问本类的所有成员, 因而它必须在类内部进行声明, 使得编译器可以正确处理他的权限.


34、介绍一下C++里面的多态?

  • 静态多态(重载, 模板): 是在编译的时候,就确定调用函数的类型。

  • 动态多态(覆盖, 虚函数实现): 在运行的时候,才确定调用的是哪个函数,动态绑定。运行基类指针指向派生类的对象,并调用派生类的函数。


  • 参考: 理解的虚函数和多态

  • 函数重载:

    • 同一可访问区域内, 存在多个不同参数列表的同名函数, 由编译器根据调用参数决定那个函数应该被调用

    • 函数重载不关心返回值类型, 但是对于函数类型时关心的, 例如类中的两个函数拥有相同参数列表的同名函数, 一个为const类型, 一个为非const类型, 依旧时属于函数重载.

  • 函数模板:

    • (模板编译)在定义模板函数时对模板本身进行编译

    • (模板实例化)在调用时对参数进行替换, 对替换参数后的代码进行编译

    • 模板函数会经历两遍编译:

    • 虽然它和函数重载类似都可以根据参数确定将要调用的函数版本, 但是函数模板只会生成将要用到的函数版本, 而函数模板无论是否调用其代码都会生成.

  • 覆盖: 是指派生类中重新定义了基类中的virtual函数

  • 隐藏:是指派生类的函数屏蔽了与其同名的基类函数,只要函数名相同,基类函数都会被隐藏. 不管参数列表是否相同。


35、用C 语言实现C++的继承

  • 关键点:

    • 使用函数指针保存函数

    • 将基类放在结构题的头部, 这样强转的就不会出错了 ```cpp #include using namespace std; //C++中的继承与多态 struct A{ virtual void fun() {//C++中的多态:通过虚函数实现 cout<<"A:fun()"<<endl; } int a; }; struct B:public A {//C++中的继承:B 类公有继承A 类 virtual void fun() { //C++中的多态:通过虚函数实现(子类的关键字virtual 可加可不加) cout<<"B:fun()"<<endl; } int b; };

36、继承机制中对象之间如何转换?指针和引用之间如何转换?

  • 派生类的对象可以当做基类对象使用, 例如赋值或则初始化等

  • 派生类对象的地址可以赋给指向基类的指针。在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。

  • 向上类型转换(派生类转基类, 总是安全的)

    • 将派生类指针或引用转换为基类的指针或引用被称为向上类型转换,向上类型转换会自动进行,而且向上类型转换是安全的。

  • 向下类型转换(基类转派生类, 不安全)

    • 将基类指针或引用转换为派生类指针或引用被称为向下类型转换,向下类型转换不会自动进行,因为一个基类对应几个派生类,所以向下类型转换时不知道对应哪个派生类,所以在向下类型转换时必须加动态类型识别技术。

    • RTTI技术,用dynamic_cast进行向下类型转换, 只有存在虚函数的类才能使用RTTI


  • 参考:

    • 浅谈C++类型转换的安全性 - freshman94的博客 - CSDN博客

    • 继承的赋值兼容规则update


37、组合与继承优缺点?

  • 继承: 继承是Is a 的关系,比如说Student 继承Person,则说明Student is a Person。

  • 继承的优点: 是子类可以重写父类的方法来方便地实现对父类的扩展。

  • 继承的缺点有以下几点:

    • ①:父类的内部细节对子类是可见的。(可以自己调用父类的方法)

    • ②:子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为。

    • ③:如果对父类的方法做了修改的话(比如增加了一个参数),则子类的方法必须做出相应的修改。所以说子类与父类是一种高耦合,违背了面向对象思想。


  • 组合(嵌入式对象): 组合也就是设计类的时候把要组合的类的对象加入到该类中作为自己的成员变量。

  • 组合的优点:

    • ①:当前对象只能通过所包含的那个对象去调用其方法,所以所包含的对象的内部细节对当前对象时不可见的。(必须通过嵌入式对象调用嵌入式对象的方法)

    • ②:当前对象与包含的对象是一个低耦合关系,如果修改包含对象的类中代码不需要修改当前对象类的代码。

    • ③:当前对象可以在运行时动态的绑定所包含的对象。可以通过set 方法给所包含对象赋值。

  • 组合的缺点:

    • ①:容易产生过多的对象。

    • ②:为了能组合多个对象,必须仔细对接口进行定义。


  • 参考: 继承的优点和缺点


38、左值右值

  • 参考: 什么是右值引用,跟左值又有什么区别?https://blog.csdn.net/zzxiaozhao/article/details/102943714

39、移动构造函数

  • 右值的概念: 将亡值, 不具名变量

  • 右值引用

    • 转移语意

    • 精确语意传递(参数列表分别为左值引用右值引用形成参数重载)

    • 概念: 其本身是一个左值, 但是它绑定了一个右值, 此右值的生命周期将和此右值引用一致.

    • 优点:

  • 移动构造函数:

    • 避免了无畏的对下销毁和构造的开销

    • 当该类对象申请了堆内存, 并在析构函数中进行释放时, 使用拷贝构造函数可能会存产生也野指针, 而使用移动构造可以避免野指针的产生.

    • 移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的初值是一个右值引用。

    • 也就是说,只用一个右值,或者将亡值初始化另一个对象的时候,才会调用移动构造函数。

    • 作为参数的右值将不会再调用析构函数。

    • `move`语句,就是将一个左值变成一个将亡值。

    • 概念: 当我们使用一个即将消亡的对象A初始化对象B时, 使用移动语意可以避免额外的无意义的复制构造操作, 也避免了释放内存, 新分配内存的开销.

    • 实现:

    • 优点

40、C 语言的编译链接过程?

  • 源代码-->预处理-->编译-->优化-->汇编-->链接–>可执行文件

  • 参考: 源码到可执行文件的过程https://blog.csdn.net/zzxiaozhao/article/details/102990773


原文链接: https://wangpengcheng.github.io/2019/12/11/interview_c_plusplus/

推荐:

面试常问的 C/C++ 问题,你能答上来几个?

C++ 面试必问:深入理解虚函数表

很多人搞不清 C++ 中的 delete 和 delete[ ] 的区别

看懂别人的代码,总得懂点 C++ lambda 表达式吧

Java、C++ 内存模型都不知道,还敢说自己是高级工程师?

C++ std::thread 必须要熟悉的几个知识点

现代 C++ 并发编程基础

现代 C++ 智能指针使用入门

c++ thread join 和 detach 到底有什么区别?

C++ 面试八股文:list、vector、deque 比较

C++经典面试题(最全,面中率最高)

C++ STL deque 容器底层实现原理(深度剖析)

STL vector push_back 和 emplace_back 区别

了解 C++ 多态与虚函数表

C++ 面试被问到的“左值引用和右值引用”

-- END --


进技术交流群,扫码添加我的微信:Byte-Flow



获取相关资料和源码


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter