【7】C++岗位求职面试八股文系列文章(语言基础)

第一篇:语言基础

第二篇:设计模式

第三篇:数据库

第四篇:计算机网络

第五篇:操作系统

第六篇:LInux

第七篇:数据结构

第八篇:智力题

[121]说说构造函数有几种,分别什么作用

C++中的构造函数可以分4类:默认构造函数、初始化构造函数、拷贝构造函数、移动构造函数。1.默认构造函数和初始化构造函数。 在定义类的对象的时候,完成对象的初始化工作。

2. 拷贝构造函数

拷贝构造函数默认实现的是值拷贝(浅拷贝)3.转换构造函数。用于将其他类型的变量,隐式转换为本类对象。下面的转换构造函数,将int类型的r转换为Student类型的对象,对象的age为r,num为1004.

[122]只定义析构函数,会自动生成哪些构造函数

只定义了析构函数,编译器将自动为我们生成拷贝构造函数和默认构造函数

默认情况下,c++编译器至少给一个类添加3个函数1.默认构造函数(无参,函数体为空)2.默认析构函数(无参,函数体为空)3.默认拷贝构造函数,对属性进行值拷贝(值拷贝)构造函数调用规则如下:如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造如果用户定义拷贝构造函数,c++不会再提供其他构造函数

[123]类成员初始化方式?

在创建对象,调用构造函数时候进行初始化1.赋值初始化,通过在函数体内进行赋值初始化;2.列表初始化,在冒号后使用初始化列表进行初始化。

这两种方式的主要区别在于:对于在函数体中初始化,是在所有的数据成员被分配内存空间后才进行初始化的。列表初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行

方法一是在构造函数当中做赋值的操作,而方法二是做纯粹的初始化操作。我们都知道,C++的赋值操作是会产生临时对象的。临时对象的出现会降低程序的效率。

[124]构造函数的执行顺序 ?

① 虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。② 基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。③ 类类型的成员对象的构造函数(按照初始化顺序)④ 派生类自己的构造函数。

虚基类构造》子类静态成员构造》基类构造》子类非静态成员构造》子类构造

[125]有哪些情况必须用到成员列表初始化?作用是什么

必须使用成员初始化的四种情况① 当初始化一个引用成员时;② 当初始化一个常量成员时;③ 当调用一个基类的构造函数,而它拥有一组参数时;④ 当调用一个成员类的构造函数,而它拥有一组参数时;

第一第二是因为:const对象或引用只能初始化但是不能赋值。构造函数的函数体内只能做赋值而不是初始化,因此初始化const对象或引用的唯一机会是构造函数函数体之前的初始化列表中。

第三第四是因为:主要是性能问题,如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数。对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,使用初始化列表少了一次调用默认构造函数的过程,(因为构造函数是赋值操作,会创建临时对象)这对于数据密集型的类来说,是非常高效的

[126]为什么用成员初始化列表会快一些?

1.初始化列表会快一些的原因是,对于类型,它少了一次调用构造函数的过程,而在函数体中赋值则会多一次调用。而对于内置数据类型则没有差别。

举个例子如果是在构造函数体内进行赋值的话,等于是一次默认构造加一次赋值,而初始化列表只做一次赋值操作。(因为赋值初始化会创建临时对象)

2.成员初始化列表做了什么① 编译器会一一操作初始化列表,以适当的顺序在构造函数之内安插初始化操作,并且在任何显示用户代码之前;② 初始化顺序是由类中的成员声明顺序决定的,不是由初始化列表的顺序决定的

[127]如何阻止一个类被实例化?有哪些方法

1.将类定义为抽象基类,抽象类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。2.或者将构造函数声明为private;

[128]如何禁止程序自动生成拷贝构造函数?(默认的)

拷贝构造函数?(默认的)

1 为了阻止编译器默认生成拷贝构造函数和拷贝赋值函数,我们需要手动去重写这两个函数,某些情况下,为了避免调用拷贝构造函数和拷贝赋值函数,我们需要将他们设置成private,防止被调用。2 类的成员函数和friend函数还是可以调用private函数,如果这个private函数只声明不定义,则会产生一个连接错误;3 针对上述两种情况,我们可以定一个base类,在base类中将拷贝构造函数和拷贝赋值函数设置成private,那么派生类中编译器将不会自动生成这两个函数,且由于base类中该函数是私有的,因此,派生类将阻止编译器执行相关的操作

[129]Debug和Release的区别是什么吗

 调试版本,包含调试信息,所以容量比Release大很多,并且不进行任何优化 发布版本,不对源代码进行调试,编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的。

[130]写一个比较大小模板

[131]说说一个类,默认会生成哪些函数

无参的构造函数、拷贝构造函数、析构函数、赋值运算函数

[132]说说 C++ 类对象的初始化顺序,有多重继承情况下的顺序

综上可以得出,初始化顺序:

父类构造函数–>子类成员类对象构造函数–>子类构造函数其中成员变量的初始化与声明顺序有关,构造函数的调用顺序是类派生列表中的顺序。析构顺序和构造顺序相反。

继承中非静态同名成员处理

如果子类中出现和父类同名的成员函数,子类成员函数隐藏掉所有父类同名成员函数,要想调用,加上父类作用域。

[133]简述 向上转型和向下转型

子类转换为父类:向上转型,使用dynamic_cast(expression),这种转换相对来说比较安全不会有数据的丢失;

父类转换为子类:向下转型,可以使用强制转换,这种转换时不安全的,会导致数据的丢失,原因是父类的指针或者引用的内存中可能不包含子类的成员的内存。

[134]简述下深拷贝和浅拷贝,如何实现深拷贝

浅拷贝:又称值拷贝,浅拷贝只是拷贝一个指针,并没有新开辟一个地址,拷贝的指针和原来的指针指向同一块地址,如果原来的指针所指向的资源释放了,那么再释放浅拷贝的指针的资源就会出现错误。

深拷贝,拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指向了不同的内存位置。并且里面的内容是一样的,两个指针先后去调用析构函数,分别释放自己所指向的位置。即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误。

深拷贝的实现:深拷贝的拷贝构造函数和赋值运算符的重载传统实现:

[135]简述一下 C++ 中的多态

派生类重写基类方法,然后用基类指针指向派生类对象,调用方法时候会进行动态绑定,这就是多态。 多态分为静态多态和动态多态

多态分为两类

  • 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态: 派生类和虚函数实现运行时多态

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • **动态多态的函数地址晚绑定 - 运行阶段确定函数地址
  1. 静态多态:编译器在编译期间完成的,编译器会根据实参类型来推断该调用哪个函数,如果有对应的函数,就调用,没有则在编译时报错。 动态多态:其实要实现动态多态,需要几个条件——即动态绑定条件: 1.虚函数。基类中必须有虚函数,在派生类中必须重写虚函数。 2.通过基类类型的指针来调用虚函数。

3.派生类对象的指针或引用可以绑定在基类对象上。在调用虚函数时,就会去查找该对象的虚函数表。查找该虚函数表中该函数的指针进行调用。协变:协变是重写的特例,基类虚函数中返回值是基类类型的引用或指针,在派生类中,返回值为派生类类型的引用或指针。

[136]说说为什么要虚析构,为什么不能虚构造

虚析构:将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。如果基类的析构函数不是虚函数,在特定情况下会导致派生来无法被析构。

1.用派生类类型指针绑定派生类实例,析构的时候,不管基类析构函数是不是虚函数,都会正常析构

2.用基类类型指针绑定派生类实例,析构的时候,如果基类析构函数不是虚函数,则只会析构基类,不会析构派生类对象,从而造成内存泄漏。为什么会出现这种现象呢,个人认为析构的时候如果没有虚函数的动态绑定功能,就只根据指针的类型来进行的,而不是根据指针绑定的对象来进行,所以只是调用了基类的析构函数;如果基类的析构函数是虚函数,则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。

C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。

虚析构:如果不设置虚析构,其实当子类没有把内存开辟到堆区时候,也不会走子类的析构函数,但是没啥影响;反而有数据开辟到堆区,就必须要走析构,所以才需要在父类中加虚析构函数。

纯虚析构也要实现(因为有可能父类数据也有开辟到堆区,也需要释放),也是抽象类

[137]虚函数表里存放的内容是什么时候写进去的(虚表写入时机)

虚函数表是一个存储虚函数地址的数组,(指针数组)。虚表(vftable)在编译阶段生成,对象内存空间开辟以后,写入对象中的 vfptr,然后调用构造函数。即:虚表在构造函数之前写入

虚表:编译阶段形成对象生成:运行阶段:开辟内存空间----往虚表里写入虚函数地址----调用构造函数

虚表的二次写入机制:每个类只有一张虚表,类的对象共享类的虚表,并且通过虚表的二次写入机制,让每个对象的虚表指针都能准确的指向到自己类的虚表,为实现动多态提供支持。

C++中虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区。

[138]虚表指针vptr的初始化时间

对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存在对象内存布局的最前面。

[139]虚构造可以吗

链接不可以

  1. 构造函数目标明确,无需通过虚函数实现(定义)构造函数用于初始化对象,c++在编译期间就明确要创建的对象的具体类型,虚函数的存在是因为编译期间没法确定具体调用的对象类型,才会有虚函数和虚函数产生。
  2. 虚函数使用条件为:对象类型完整(构造调用之后)(使用)虚函数的调用需要使用到虚表指针,通过虚表指针找到虚表。而虚表指针是创建对象时才会将其地址给对象内存的的。

细节:编译器生成虚表-(指针数组)》开辟内存-》写入虚表(吧虚表写入到内存中)-》构造函数-》调用虚函数

[140]构造函数内部调用虚函数(语法可以,但是达不到效果)

语法可以:因为在构造函数内部可以看到虚表,那么就可以找到虚函数fun的地址达不到效果:派生类对象构造期间进入基类的构造函数时,对象类型变成了基类类型,如果是基类调用虚函数,虚函数始终仅仅调用基类的虚函数(派生类还没有构造),不能达到多态的效果。

[续]C++岗位求职面试八股文第八篇

更多关于算法题解、软件开发面经、机器学习算法面经、各企业面试问题记录,关注Fintech砖,持续更新中。https://www.nowcoder.com/users/873777317

企业面试记录专栏https://www.nowcoder.com/creation/manager/columnDetail/0YBWnm

机器学习面经专栏https://www.nowcoder.com/creation/manager/columnDetail/j8nNy0

软件开发面经专栏https://www.nowcoder.com/creation/manager/columnDetail/0aXKaM

更多校园招聘常见面试问题(开发、算法、编程题目)参见CSDN博客:http://t.csdn.cn/V4qbH

欢迎关注、收藏、点赞后进行问题咨询及秋招建议!

#晒一晒我的offer##牛客在线求职答疑中心##我的求职思考##软件开发薪资爆料##我的实习求职记录#
软件开发八股面经 文章被收录于专栏

包含C++、操作系统、数据库、计算机组成、计算机网络、设计模式、操作系统、牛客网服务器项目、综合智力题等

全部评论
感谢分享,加油牛友!😁
点赞 回复 分享
发布于 2024-04-23 19:15 山东

相关推荐

05-09 13:22
门头沟学院 Java
点赞 评论 收藏
分享
03-18 09:45
莆田学院 golang
牛客749342647号:佬,你这个简历模板是哪个,好好看
点赞 评论 收藏
分享
评论
2
10
分享

创作者周榜

更多
牛客网
牛客企业服务