C++面试——进阶篇

1、什么情况下会调用拷贝构造函数

  1. 用类的一个对象去初始化另一个对象的时候: new Object{obj}
  2. 对一个类对象初始时进行赋值操作:Object newObj = obj;
  3. 当函数的参数是类的对象时,就是值传递的时候 : void func(Object obj)
  4. 当函数的返回值是类的对象或者引用的时候 : Object getObject()

        注意:编译开了RVO(返回值优化)时,3 和 4 可能并不会发生!

2、 左值和右值的区分

        最简单的判断方式:左值有名字右值没有名字!

                int a = 1;// a为左值, 1为右值

        如果一个右值有了名字,就会变成左值!比如右值当作形参传递时。

 3、编译器默认会生成哪些函数

  1. 默认构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值函数

注意:如果自己定义了有参构造函数,编译器不会再生成默认构造函数

4、 重载overload覆盖override重写overwrite这三者之间的区别

  1.  overload,将语义相近的几个函数用同一个名字表示,但是参数和返回值不同,这就是函数重载(相同范围里函数名字相同、参数不同)
  2. override,派生类覆盖基类的虚函数,实现接口的重用(基类和派生类中函数名字相同、参数相同、基类函数为虚函数)
  3. overwrite,派生类屏蔽了其同名的基类函数(基类和派生类中函数名字相同、参数相同、基类函数不是虚函数)

 5、什么是接口类,以及接口类的作用

接口类主要用来实现继承时定义相关接口的,是对子类的约束!

一个类中有纯虚函数即为接口类(也叫纯虚类),接口类只能被继承,无法实例化!

6、如何防止类被实例化,这种类的意义是什么

  1. 添加一个无用的纯虚方法
  2. 构造函数设置为非public
  3. 删除构造函数 

意义:

  1. 定义一些算法类、工具类,一般此类中的方法为static方法
  2. 实现自己内存管理机制(例如通过static方法、友元函数来创建对象)
  3. 定义接口

7、介绍下友元的特性

  1. 友元关系不能被继承。 
  2. 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。 
  3. 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
  4. 友元函数的定义默认是扩展到外部的

8、内存对齐的原理于意义

        结构体以及类成员属性的字节对齐,意义就是减少cpu读取的次数,提高程序运行效率。比如一个int变量长度为4个字节,cpu一次读4个字节,当然是一次读取比较好。

        但是如果前面有一个char,地址为0-1。那么这个int的地址就为1-4。导致cpu,分两次读取int值。
        如果要取消字节对齐,使用#pragma pack(1),也就是使用1字节对齐!(一般在网络传输时取消对齐)

 9、构造函数为什么一般不定义为虚函数?而析构函数一般写成虚函数的原因 ?

构造函数不能声明为虚函数

  1. 因为创建一个对象时需要确定对象的类型,而虚函数是在运行时确定其类型的。而在构造一个对象时,由于对象还未创建成功,编译器无法知道对象的实际类型,是类本身还是类的派生类等等
  2. 虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数即构造函数了

析构函数最好声明为虚函数

  1. 首先析构函数可以为虚函数,当析构一个指向派生类的基类指针时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。
  2. 如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除指向派生类的基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。

 10、引用作为函数参数以及返回值的好处及注意事项

引用传参的好处:

  1. 在函数内部可以对此参数进行修改
  2. 提高函数调用和运行的效率(所以没有了传值和生成副本的时间和空间消耗)

注意事项:

  1. 不能返回局部变量的引用。因为函数返回以后局部变量就会被销毁
  2. 不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak
  3. 可以返回类成员的引用,但是最好是const。因为如果其他对象可以获得该属性的非常量的引用,那么对该属性的单纯赋值就会破坏业务规则的完整性。

11、为什么推荐使用auto关键字

  1. 避免忘记初始化,未初始化的对象可能会导致难以预见的错误!(例如auto x;无法编译通过)
  2. 避免不必要的代码书写,甚至能帮你避免错误的类型转换!
  3. 重构代码更简单!(例如 int x = 1; 改为long long x = 123LL; 使用auto 则不需要修改类型)

12、constexpr VS const

  1. 假如你将一个成员函数标记为constexpr,则顺带也将它标记为了const。
  2. constexpr是编译时常量(可以作为元编程中的元数据),const是运行时常量
  3. constexpr函数被应用在调用宏的所有场合。例如,你想要一个计算数组size的函数,size是10的倍数。如果不用constexpr,你需要创建一个宏或者使用模板,因为你不能用函数的返回值去声明数组的大小。但是用constexpr,你就可以调用一个constexpr函数去声明一个数组。
  4. if constexpr 可以用来做条件编译

13、引用与指针有什么区别

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

 14、explicit关键字

  1. 规避可被单参调用的构造函数引起的隐式类型转换
  2. 所有的智能指针类都有一个explicit构造函数,以指针作为参数.因此不能自动将指针转换为智能
  3. 指针对象,必须显式调用

 15、创建对象时()与 {} 的区别

  1. {} 初始化可以应用的语境最宽泛,可以阻止隐式窄化型别转换,还对最令人苦恼的解析语法免疫
  2. 在构造函数重载决议期,只要有任何可能,大括号初始化就会与带有std::initializer_list型别的形参相匹配,即使其他重载版本有着貌似更加匹配的形参表
  3. std::vector<int>(10, 10) (创建一个含有10个元素的vector,所有元素都是10)与 std::vector<int>{10, 10} (创建了一个含有2个元素的vector,两个元素为10和20)效果完全不一样。

      总结一条:尽量使用 {} 来创建对象。

 16、nullptr、NULL、0

  1. c++使用nullptr来表示空指针。形如:0x0
  2. NULL时不确定的,可能时0L,也可能时0,这和NULL的实现相关
  3. 0就是字面量int
  4. 空指针务必使用nullptr,特别是是在做类型推导时,例如auto、模板参数
  5. NULL和0可能会导致重载混乱,nullptr不会(nullptr不会隐式转换为int)
  6. 避免在整型和指针型之间的重载

17、delete的作用和private的区别

  1. 两者都能使成员函数无法被外部访问
  2. delete不仅仅能删除成员函数,还能删除普通函数
  3. delete还可以删除模板函数,例如某些特化模板函数可以删除以禁止调用

18、lambda实现原理和使用注意事项

  1. lambda底层实现是仿函数,也就是重载了operator()方法的类
  2. lambda参数捕获需要注意捕获参数的生命期,防止引用、指针空悬
  3. this指针捕获是值捕获

19、介绍一下智能指针和实现原理,以及注意事项

  1. 智能指针是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。
  2. 智能指针将一个计数器与指向的指针相关联;当智能指针发生拷贝、赋值等操作时会更新计数器,如果引用计数减至0,则释放指向的指针。
  3. unique_ptr:独立维护一个指针,不允许拷贝和赋值(拷贝构造和赋值运算符被禁用),但是可以转移指针所有权。
  4. shared_ptr:多个shared_ptr可以维护相同的指针,通过引用计数来维护指针的生命周期,循环引用可能导致死锁。
  5. weak_ptr:使用时配合share_ptr使,解决两个share_ptr互相引用产生死锁,计数永远降不到0,没办法进行资源释放,造成内存泄漏的问题。
  6. 创建对象的时候用shared_ptr强智能指针,别的地方一律用

20、函数重载

  1. 重载判决依据:函数名相同,参数不同(个数不同、类型不同、顺序不同)
  2. 判定重载与返回值无关

21、++/--操作符重载

  1. 后置++操作符需要一个int类型的占位参数 : A operator++(int)
  2. 前置++操作符不需要额外的参数 : A& operator++()

22、成员函数重载修饰符const、&、&&

  1. const、&、&& 修饰成员函数时,都是修饰this指针,所以,可以用来作为函数重载的判决依据
  2. const,const&,const&& 分别对应的是不同类型
  3. 带修饰符的成员函数与无修饰符的函数是两个不同函数,override时一定要分清楚

23、const

  1. const修饰成员函数时,则该函数体内不能对成员属性进行修改,也不可以访问非const方法
  2. const方法要修改成员属性时,可以将成员属性设置为 mutable
  3. const属性务必在参数初始化列表中进行初始化

未完,不定时补充。。。。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chls

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值