暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

C++面向对象之面经

戏命流沙 2022-05-11
887

各位客官久等了,本文将主要讲讲关于C++面向对象常见的面试问题。


  1. C++有哪些性质?

    1. 封装:将客观事物抽象成类,每个类对自身的数据和方法。封装可以使得代码模块化,目的是为了代码重用
    2. 继承:子类继承父类的方法和属性,继承可以扩展已存在的代码,目的是为了代码重用
    3. 多态:允许将子类类型的指针赋值给父类类型的指针。

  2. 重载、隐藏、重写(覆盖)三者的区别?

    参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型(统一为void,否则报错)。

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

    重写(覆盖):是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。

    重载的参数不同,函数体不同;隐藏的参数可以不同,函数体不同;重写或者覆盖仅仅函数体不同。重载根据名字可以猜测一定有重新的东西加进来;覆盖,根据这个名字就能联想到要一模一样才能覆盖。隐藏的话只要盖上就行,不一定非要一模一样。

  3. 重载为什么改变参数就可以实现调用不同的函数?

    因为C++在编译的时候会对函数进行重命名,保证函数名的唯一性,而重载函数的参数不同,就会被命名为不同的函数名。

  4. 引用与指针有什么区别?

    1) 引用必须被初始化,指针不必。

    2) 引用初始化以后不能被改变,指针可以改变所指的对象。

    3) 不存在指向空值的引用,但是存在指向空值的指针

  5. 引用与多态的关系?

          引用就是对象的别名。引用主要用作函数的形参。引用必须用与该引用同类型的对象初始化:引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。
    int ival = 1024;int &refVal = ival; const 对象的引用只能是const类型的:const int ival = 1024;const int &refVal = ival; 多态是通过虚函数实现的。

  6. 构造函数可以被重载么?析构函数呢?

       构造函数可以被重载,因为构造函数可以有多个且可以带参数。析构函数不可以被重载,因为析构函数只能有一个,且不能带参数。构造函数主要用来在创建对象时完成对对象属性的一些初始化等操作, 当创建对象时, 对象会自动调用它的构造函数。一般来说, 构造函数有以下三个方面的作用:

1、给创建的对象建立一个标识符;

2、为对象数据成员开辟内存空间;

3、完成对象数据成员的初始化。

7.什么是多态机制?

      多态就是说同一个名字的函数可以有多种不同的功能。分为编译时的多态和运行时的多态。编译时的多态就是函数重载,包括运算符重载,编译时根据实参确定调用哪个函数。运行时的多态则和虚函数、继承有关。

8.多态底层实现是怎么样的?

       利用虚函数表,先构建一个基类,然后在基类的构造函数中会建立虚函数表,也就是一个储存虚函数地址的数组,内存地址的前四个字节保存指向虚函数表的指针,然后当多个子类继承父类之后,主函数中可以通过父类指针调用子类的继承函数。虚函数表属于类,也属于它的子类等各种派生类。虚函数表由编译器在编译时生成,保存在.rdata只读数据段。

那子类的多态函数是怎么被调用的?

       因为每个子类都继承并设置了自己的虚函数表,每次用用父类指针创建新子类时就会出现,从而最终调用自己的表。

怎么知道多态时,指向哪个虚函数?

       定义的父类指针new出哪个子类就是指向哪个子类的虚函数。

对象怎么找到对应的虚函数表?

       ( *( *(p+0) + 0 ) )(p); p是父类指针,p+0是指针vfptr,该指针指向虚函数表的首地址。所以*(p+0)即虚函数表的地址,( *(p+0) + 0 )就是要找的虚函数的地址,+0代表该虚函数在表中的偏移量。( *( *(p+0) + 0 ) )(p)表示对该函数的调用。

虚函数表的结构是怎样的?

      虚函数表是一个函数指针数组,数组里存放的都是函数指针,指向虚函数所在的位置。对象调用虚函数时,会根据虚指针找到虚表的位置,再根据虚函数声明的顺序找到虚函数在数组的哪个位置,找到虚函数的地址,从而调用虚函数。

A,B两个类,类中有虚函数。C继承AB,有几张虚函数表?

答:2张

为什么2张?

多继承就会有多个虚函数表。因为每个父类的虚函数是不同的,指针也是不同的。

如果共用一张虚函数表,就分不清到底子类的实例化是针对哪一个基函数的。

9.什么是抽象类?

包含纯虚函数的类称为抽象类。
子类实现抽象类有哪些注意事项?
必须把基类中的所有纯虚函数都初始化实例化。子类的函数名必须和抽象类一样,参数类型也要一模一样。
那基类的所有纯虚函数都要实现吗?
对,所有的都要实现。
抽象类存在的意义,要解决什么问题?

在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。在实际开发中,你可以定义一个抽象基类,只完成部分功能,未完成的功能交给派生类去实现(谁派生谁实现)。这部分未完成的功能,往往是基类不需要的,或者在基类中无法实现的。

10.内存溢出和内存泄露有什么区别?

       内存溢出:是指申请内存时申请的空间不够,比如申请的int型的内存空间,但是存进去一个long型的数据。

内存泄漏:是指申请内存空间之后,没有释放申请的内存空间。

内存溢出的原因:

(1)内存中加载的数据量过于庞大,比如一次性从数据库中读取大量数据。
(2)代码中出现死循环或者循环产生过多重复的实体。
(3)启动参数内存值设定的过小。
(4)使用的第三方软件的bug。
内存泄漏的原因:
(1)new创建出来的对象没有及时的delete掉,导致了内存的泄露;

(2)delete一个void*的指针可能会造成内存上的泄露!因为delete一个void*的对象指针,它不会调用析构函数,如果该对象里面有指针,最后指针就会没有释放导致内存泄漏。

delete一个void*为什么不会调用析构函数?(百度搜不到,求大神解答)


(3)new创建了一组对象数组,内存回收的时候却只调用了delete而非delete []来处理,导致只有对象数组的第一个对象的析构函数得到执行并回收了内存占用,数组的其他对象所占内存得不到回收,导致内存泄露;

一般怎么处理内存泄漏?

(1)从程序内部重新编译,养成良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄漏。

(2)结束程序,内存自然就会被操作系统回收。

(3)重新启动电脑,立刻恢复。

11.函数模板与类模板有什么区别?

      函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

12.继承优缺点 

优点:继承可以方便地改变父类的实现,可以实现多态,子类可以继承父类的方法和属性。

缺点:破坏封装,子类和父类可能存在耦合。子类不能改变父类的接口。

13.C++纯虚函数,虚函数,虚函数的实现,什么是虚指针?

 纯虚函数:是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。virtual void f()=0;//是一个接口,子类必须实现这个接口虚指针或虚函数指针是虚函数的实现细节。带有虚函数的每一个对象都有一个虚指针指向该类的虚函数表。

虚函数 :虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态覆盖(Override)

纯虚函数和虚函数的区别是:纯虚函数子类必须实现。

纯虚函数的优点:

(1)可以实现多态特性
(2)定义一个标准的接口,在派生类中必须予以重写以实现多态性。
抽象类 :包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。

多态性可分为两类:静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,动态多态性是通过虚函数实现的。

为什么基类析构函数是虚函数?

编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。

为什么构造函数不能为虚函数

虚函数采用一种虚调用的方法。需调用是一种可以在只有部分信息的情况下工作的机制。如果创建一个对象,则需要知道对象的准确类型,因此构造函数不能为虚函数。

如果虚函数是有效的,那为什么不把所有函数设为虚函数?

不行。因为每个虚函数的对象都要维护一个虚函数表,因此在使用虚函数的时候都会产生一定的系统开销,这是没有必要的。

14.面向对象如何实现数据隐藏

定义类来实现数据隐藏:

成员函数和属性的类型:私有成员private,保护成员protected,公共成员public

15.深拷贝和浅拷贝的区别(举例说明深拷贝的安全性)

      如果一个类拥有资源,该类的对象发生了复制,如果资源发生了重新分配,就是深拷贝,否则就是浅拷贝

      深拷贝:该对象和原对象占用不同的存储空间,即拷贝位于stack域(栈)中的内容,又拷贝类中位于heap域(堆)中的内容

      浅拷贝:该对象和原对象占用同一块内存区域,仅拷贝类中位于stack域(栈)中的内容

      当类的成员变量中有指针时,使用深拷贝安全,如果两个对象指向同一块内存空间,当其中一个对象的删除后,该块内存空间就会被释放,另外一个对象指向的就是垃圾内存。

16.介绍C++所有的构造函数

构造函数的作用:当创建对象时,系统分配了内存空间后,会自动调用相应的构造函数


  • 默认构造函数:没有参数,如果创建了一个类,没有定义任何构造函数,系统会自动生成默认的构造函数

  • 一般构造函数:带有参数,一个类可以有若干个一般构造函数,前提是参数的数量或者类型不同(C++重载函数原理)

  • 拷贝构造函数:参数为该类的常量引用对象,如果类中没有定义拷贝构造函数,系统会默认生成一个默认的拷贝构造函数,默认生成的拷贝构造函数都是浅拷贝的

  • 赋值构造函数:区别于以上构造函数,以上构造函数都没有函数的返回类型,这里虽然称为“赋值构造函数”,其实是“重载了赋值运算符的函数”,该函数的返回类型是该类的引用类型,参数是该类的常量引用对象。

    联系邮箱:3492925248@qq.com







文章转载自戏命流沙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论