C++面试问题汇总

C++面试问题汇总

1、谈谈你对命名空间的理解

namespace 是⼀个关键字:随着⼯程量的增加,变量命名上不可避免的会出现重名,防⽌名称冲突(在两个不同的命名空间中,即使2个变量名相同,也是2个不同的变量),在实际⼯作中,基本都使⽤标准命名空间。
命名空间只能全局范围内定义,不能定义在函数内部。
命名空间内,可以存放 变量、函数、结构体、类 ;也可以嵌套其他的命名空。
命名空间可以匿名(但⼀般不这样使⽤),类似静态全局变量。
命名空间是可以起别名的。

2、谈谈指针和引⽤的区别

引⽤是给变量起别名,内部实现是指针常量(int* const ref = &a),其可以简单的理解为本体指针存放的是变量的地址。
引⽤的本质是指针常量,其指向不可修改,⽽指针可以改变指向。
引⽤创建的同时必须初始化,指针创建的时候可以不必初始化。
引⽤不能为空,指针可以为 NULL。
“引⽤变量 ref”的内存单元保存的是“被引⽤变量 a”的地址 sizeof(引⽤) = 指向变量的⼤⼩ sizeof(指针) = 指针本身的⼤⼩
引⽤使⽤的时候⽆需解引⽤,指针需要解引⽤。
指针和引⽤“⾃增/⾃减运算”意义不⼀样。

3、谈谈函数重载的条件

函数重载:在C语⾔中,函数名必须是唯⼀的,程序中不允许出现同名的函数 ,在C++中是允许出现同名的函数,即在同⼀作⽤域内,具有相同函数名,不同参数列表的⼀组函数, 称为函数重载函数。
重载实现的原理
编译器为了实现函数重载,也是默认为我们做了⼀些幕后的⼯作,编译器⽤不同的参数类型来修饰不同的函数名,⽐如void func(); 编译器可能会将函数名修饰成func,当编译器碰到void func(int x),编译器可能将函数名修饰为func_int,当编译器碰到void func(int x,char c),编译器可能会将函数名修饰为_func_int_char我这⾥使⽤”可能”这个字眼是因为编译器如何修饰重载的函数名称并没有⼀个统⼀的标准,所以不同的编译器可能会产⽣不同的内部名。
函数重载实现的条件
同⼀个作⽤域、参数的个数不同、参数类型不同、参数的顺序不同。

4、谈谈什么叫对象成员以及对象成员的构造函数调⽤⽅式

在类中定义的数据成员⼀般都是基本的数据类型。但是类中的成员也可以是对象,叫做对象成员。
C++中对对象的初始化是⾮常重要的操作,当创建⼀个对象的时候,c++编译器必须确保调⽤了所有⼦
对象的构造函数。如果所有的⼦对象有默认构造函数,编译器可以⾃动调⽤他们。但是如果⼦对象没有
默认的构造函数,或者想指定调⽤某个构造函数怎么办?
那么是否可以在类的构造函数直接调⽤⼦类的属性完成初始化呢?但是如果⼦类的成员属性是私有的,
我们是没有办法访问并完成初始化的。
解决办法⾮常简单:对于⼦类调⽤构造函数,c++为此提供了专⻔的语法,即构造函数初始化列表。 当
调⽤构造函数时,⾸先按各对象成员在类定义中的顺序(和参数列表的顺序⽆关)依次调⽤它们的构造
函数,对这些对象初始化,最后再调⽤本身的函数体。也就是说,先调⽤对象成员的构造函数,再调⽤
本身的构造函数。 析构函数和构造函数调⽤顺序相反,先构造,后析构。

5、谈谈你对explicit的理解

c++提供了关键字explicit,禁⽌通过构造函数进⾏的隐式转换。声明为explicit的构造函数不能在隐式转换中使⽤

6、谈谈c中malloc free 与 c++中的new delete有什么区别,或则c++在堆区申请对象时为啥推荐使⽤new delete

c++中的malloc free的问题:
1、程序员必须确定对象的⻓度。
2、malloc返回⼀个void指针,c++不允许将void赋值给其他任何指针,必须强转。
3、malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。
4、⽤户在使⽤对象之前必须记住对他初始化,构造函数不能显示调⽤初始化(构造函数是由编译器调⽤),⽤户有可能忘记调⽤初始化函数
总结:c的动态内存分配函数太复杂,容易令⼈混淆,是不可接受的,c++中我们推荐使⽤运算符new 和delete。
new操作符能确定在调⽤构造函数初始化之前内存分配是成功的,所有不⽤显式确定调⽤是否成功。
delete表达式先调⽤析构函数,然后释放内存。

7、谈谈你对static静态成员变量的理解

在⼀个类中,若将⼀个成员变量声明为static,这种成员称为静态成员变量。与⼀般的数据成员不同,⽆论建⽴了多少个对象,都只有⼀个静态数据的拷⻉。静态成员变量,属于某个类,所有对象共享。
静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。
注意:
1、静态成员变量必须在类中声明,在类外定义。 2、静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占空间。 3、静态数据成员可以通过类名或者对象名来引⽤。

8、谈谈你对static静态成员函数的理解

在类定义中,前⾯有static说明的成员函数称为静态成员函数。静态成员函数使⽤⽅式和静态变量⼀样,同样在对象没有创建前,即可通过类名调⽤。静态成员函数主要为了访问静态变量,但是,不能访问普通成员变量。
静态成员函数的意义,不在于信息共享,数据沟通,⽽在于管理静态数据成员,完成对静态数据成员的封装。
1、静态成员函数只能访问静态变量,不能访问普通成员变量 。
2、静态成员函数的使⽤和静态成员变量⼀样 。
3、静态成员函数也有访问权限。
4、普通成员函数可访问静态成员变量、也可以访问⾮经常成员变量。

9、谈谈你对this的理解

成员函数通过this指针即可知道操作的是那个对象的数据。This指针是⼀种隐含指针,它隐含于每个类的⾮静态成员函数中。This指针⽆需定义,直接使⽤即可。
注意:静态成员函数内部没有this指针,静态成员函数不能操作⾮静态成员变量。

10、谈谈继承中的构造与析构的顺序

1、⼦类对象在创建时会⾸先调⽤⽗类的构造函数 。
2、⽗类构造函数执⾏完毕后,才会调⽤⼦类的构造函数 。
3、当⽗类构造函数有参数时,需要在⼦类初始化列表(参数列表)中显示调⽤⽗类构造函数 。
4、析构函数调⽤顺序和构造函数相反。

11、静态多态与动态多态的区别

静态多态和动态多态的区别就是函数地址是早绑定(静态联编)还是晚绑定(动态联编)。如果函数的调⽤,在编译阶段就可以确定函数的调⽤地址,并产⽣代码,就是静态多态(编译时多态),就是说地址是早绑定的。⽽如果函数的调⽤地址不能编译不能在编译期间确定,⽽需要在运⾏时才能决定,这这就属于晚绑定(动态多态,运⾏时多态)。

12、c++的动态捆绑机制是怎样的?

⾸先,我们看看编译器如何处理虚函数。当编译器发现我们的类中有虚函数的时候,编译器会创建⼀张虚函数表,把虚函数的函数⼊⼝地址放到虚函数表中,并且在类中秘密增加⼀个指针,这个指针就是vpointer(缩写vptr),这个指针是指向对象的虚函数表。在多态调⽤的时候,根据vptr指针,找到虚函数表来实现动态绑定。

13、虚析构的作⽤

虚析构函数是为了解决基类的指针指向派⽣类对象,并⽤基类的指针删除派⽣类对象。

14、谈谈你对模板的理解

所谓函数模板,实际上是建⽴⼀个通⽤函数,其函数类型和形参类型不具体制定,⽤⼀个虚拟的类型来代表。这个通⽤函数就成为函数模板。
c++提供两种模板机制:函数模板和类模板。
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属。
模板⽤于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通⽤⾏为。

15、const与#define 相比,有何优点?

const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1.const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2. 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
3.谈谈你对拷贝构造函数和赋值运算符的认识

16、拷贝构造函数和赋值运算符重载有以下两个不同之处:

  1. 拷贝构造函数生成新的类对象,而赋值运算符不能。
  2. 由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先把内存释放掉
    注意:当有类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。

17、谈谈你对浅拷⻉与深拷⻉的区别

浅拷⻉
同⼀类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独⽴的两个对象,
这种情况被称为浅拷⻉. ⼀般情况下,浅拷⻉没有任何副作⽤,但是当类中有指针,并且指针指向动态
分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
深拷⻉
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要⾃定义拷⻉构造函数,
⾃⾏给指针动态分配空间。

STL标准模板库

1、谈谈STL的六⼤组件

容器:各种数据结构,如vector、list、deque、set、map等,⽤来存放数据,从实现⻆度来看,STL容器是⼀种class template。
算法:各种常⽤的算法,如sort、find、copy、for_each。从实现的⻆度来看,STL算法是⼀种functiontempalte。
迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现⻆度来看,迭代器是⼀种将operator* , operator-> , operator++,operator–等指针相关操作予以重载的class template. 所有STL容器都附带有⾃⼰专属的迭代器,只有容器的设计者才知道如何遍历⾃⼰的元素。原⽣指针(nativepointer)也是⼀种迭代器。
仿函数:⾏为类似函数,可作为算法的某种策略。从实现⻆度来看,仿函数是⼀种重载了operator()的class 或者class template
适配器:⼀种⽤来修饰容器或者仿函数或迭代器接⼝的东⻄。
空间配置:负责空间的配置与管理。从实现⻆度看,配置器是⼀个实现了动态空间配置、空间管理、空间释放的class tempalte。

2、vector容器相⽐于普通的数组array有啥优点

vector的数据安排以及操作⽅式,与array⾮常相似,两者的唯⼀差别在于空间的运⽤的灵活性。Array是静态空间,⼀旦配置了就不能改变,要换⼤⼀点或者⼩⼀点的空间,可以,⼀切琐碎得由⾃⼰来,⾸先配置⼀块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加⼊,它的内部机制会⾃动扩充空间以容纳新元素。

3、deque容器相对于vector容器的区别

Deque容器和vector容器最⼤的差异
⼀、在于deque允许使⽤常数项时间对头端进⾏元素的插⼊和删除操作。
⼆、在于deque没有容量的概念,因为它是动态的以分段连续空间组合⽽成,随时可以增加⼀段新的空间并链接起来,换句话说,像vector那样,”旧空间不⾜⽽重新配置⼀块更⼤空间,然后复制元素,再释放旧空间”这样的事情在deque身上是不会发⽣的。也因此,deque没有必须要提供所谓的空间保留(reserve)功能。

4、谈谈stack容器的概念

Stack所有元素的进出都必须符合”先进后出”的条件,只有stack顶端的元素,才有机会被外界取⽤。Stack不提供遍历功能,也不提供迭代器

5、谈谈queue容器的概念

Queue是⼀种先进先出(First In First Out,FIFO)的数据结构,它有两个出⼝,queue容器允许从⼀端新增元素,从另⼀端移除元素。
Queue所有元素的进出都必须符合”先进先出”的条件,只有queue的顶端元素,才有机会被外界取⽤。
Queue不提供遍历功能,也不提供迭代器。

6、谈谈list容器的概念

相较于vector的连续线性空间,list就显得负责许多,它的好处是每次插⼊或者删除⼀个元素,就是配置或者释放⼀个元素的空间。因此,list对于空间的运⽤有绝对的精准,⼀点也不浪费。⽽且,对于任何位置的元素插⼊或元素的移除,list永远是常数时间。
1、采⽤动态存储分配,不会造成内存浪费和溢出
2、链表执⾏插⼊和删除操作⼗分⽅便,修改指针即可,不需要移动⼤量元素
3、链表灵活,但是空间和时间额外耗费较⼤

7、谈谈set/multiset容器的概念

Set的特性是。所有元素都会根据元素的键值⾃动被排序。Set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值⼜是实值。Set不允许两个元素有相同的键值。
multiset特性及⽤法和set完全相同,唯⼀的差别在于它允许键值重复。set和multiset的底层实现是红⿊树,红⿊树为平衡⼆叉树的⼀种。

8、谈谈map/multimap容器的概念

Map的特性是,所有元素都会根据元素的键值⾃动排序。Map所有的元素都是pair,同时拥有实值和键值,pair的第⼀元素被视为键值,第⼆元素被视为实值,map不允许两个元素有相同的键值。
Multimap和map的操作类似,唯⼀区别multimap键值可重复。
Map和multimap都是以红⿊树为底层实现机制。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值