C++面试知识点

@TOC


前言

C++面试知识点。

一、C和C++的区别?

面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
缺点:性能比面向过程低
关键字、后缀名、返回值
C语言中,如果一个函数没有指定返回值类型,默认返回int类型
C++中,如果一个函数没有返回值则必须指定为void

二、C++里面的指针和引用的区别?

指针 引用
指针是地址、有存储空间 就是别名
指针可以有多级,可以指向NULL 引用只能是一级,不能指向NULL
指针可以在定义的时候不初始化,在初始化之后可以再改变指向 引用必须在定义的时候初始化,在初始化之后不可以再改变指向
sizeof的结果是4或8 sizeof的结果是被引用对象的大小
指针作为参数传递时,要检查是否为空 引用作为参数传递时,不需要检查是否为空
指针自加1是将指针指向向后偏移一个存储单元 引用自加1是将对应的值加1
引用比指针多了类型检查

【注】引用在底层是怎么实现的?
引用的底层是通过指针实现的,引用的本质就是所引用对象的地址。

int * const aref = &a;
*aref = 20;

【注】引用能不能引用空类型(void类型)?
引用为对象起了另外的一个名字,该对象是已经存在的对象,引用必须初始化,有类型
void类型没有分配内存,而引用必须是另一个固定内存变量的别名,所以不能指向void

三、C++里面的static关键字?

一、C++里面的static关键字的作用?

两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

面向过程:全局静态变量、局部静态变量、静态函数。
面向对象:类的静态数据成员(对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。)

类的静态成员函数:
非静态成员函数可以任意地访问静态成员函数和静态数据成员;
静态成员函数不能访问非静态成员函数和非静态数据成员;
没有this指针的额外开销。

【注】静态的成员变量放在内存的哪个区?
全局数据区。

二、静态的成员函数只能调用静态的成员方法,为什么呢?或者说为什么不能在静态成员函数中使用非静态变量?

程序最终都将在内存中执行,变量只有在内存中占有一席之地时才能被访问。
因为静态是针对类的,而成员变量为对象所有

静态成员函数不属于任何一个类对象,没有this指针,而非静态成员必须随类对象的产生而产生,所以静态成员函数”看不见”非静态成员,自然也就不能访问了。
类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;
非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

在一个类的静态成员中去访问其非静态成员之所以会出错,是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

【注】静态的局部变量,是一个数据成员,放在内存的哪个区?
全局数据区(静态存储区)。

四、C++里面的const有什么作用?假如说这个const修饰的是类的成员函数,会怎么样?

1、const用于定义常量:const定义的常量,编译器可以对其进行数据静态类型安全检查
2、const修饰函数形式参数:当输入参数为用户自定义类型和抽象数据类型时,应该将值传递改为const &传递,可以提高效率

【注】用引用传递不需要产生临时对象,节省了临时对象的构造、复制、析构过程消耗的时间。但是只有引用有可能会改变a,所以加const。

3、const修饰函数的返回值:如给指针传递的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰的同类型的指针

4、const修饰类的成员函数(函数定义体):任何不会修饰数据成员的函数都应用const修饰,这样,当它不小心修改了数据成员或调用了非const成员函数时,编译器都会报错

【注】const修饰类的成员函数形式为:int GetCount(void) const;

【注】const和static这两个关键字可以同时使用吗?
不可以。const要在定义的时候进行初始化。
假设在类里面定义了一个const static的类型,这样的话会有什么问题吗?或者考虑另外一个问题:类里面的静态成员变量应该怎么初始化呢?
类内定义,类外初始化。

五、const和#define有什么区别?一般推荐使用哪种?或者说在某个场景下用两个都可以,那你会用哪种?

define和const都是定义作用

1、#define不带类型,const带类型;

2、#define不占内存,const占内存;

【注】#define只在预处理阶段起作用,它定义的宏在编译后消失了,不占用内存,但是const定义的常变量本质上是一种变量,在编译运行阶段使用的,有类型、占用存储单元。

3、#define只是简单的字符替换,可能会出现问题,不如const安全。

【注】typedef 用来定义一种数据类型的别名,增强程序的可读性。
typedef 是编译过程的一部分,有类型检查的功能。
typedef 有作用域限定。

【注】尽量用编译器而不用预处理:即尽量用const和inline而不用#define。

六、C++里面的inline关键字知道吗?这个inline关键字有什么地方是不能使用的?

在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。

inline的使用是有所限制的:
inline只适合函数体内代码简单的函数使用,
不能包含复杂的结构控制语句例如while、switch,
并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。

内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率
【注】如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。

【注】建议:inline函数的定义放在头文件中
编译器必须随处可见内联函数的定义,所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。

七、C++里面的函数指针了解吗?一般在什么场景下会用到这个?

每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针
指向函数的指针变量的一般定义形式为:

数据类型 (*指针变量名)(参数表);
int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型
int *p(int a, int b);   //p是函数名,此函数的返回值类型为整型指针

特点:
函数指针可任意指向与其返回类型和参数列表相同的函数
函数指针不支持+1, ++, --等操作
使用typedef可以创建函数指针的别名。

函数指针常用的用法/场景:将函数作为参数传递
函数指针的应用场景:回调(callback)。我们调用别人提供的 API函数(Application Programming Interface,应用程序编程接口),称为Call;如果别人的库里面调用我们的函数,就叫Callback。

八、C++里面的多线程有了解吗?多线程里面的锁都有哪些类型?

互斥锁、读写锁、条件变量等。

九、vector的底层是怎么实现的?

vector同样是一个模板,并且其底层实现用的是三个指针,然后利用这三个指针相互加减,达到存储效果。

而vector和string类似,本质是都是一个顺序表。

1、尾插push_back()
该接口的实现操作一般是先检查容量是否充足,然后把数据放进去,最后size大小加一。

2、尾删pop_back()
实现该接口的操作一般是先检查是否还存在数据,然后size大小减一。

3、某一位置插入 insert()
同样的道理,一般先检查容量是否充足,如果不够,需要警惕迭代器失效问题,然后移动该位置及以后的所有数据,最后插入数据;
官方文档定义其返回值为新插入数据的位置。

4、某一位置删除 erase()
该接口的操作一般是从pos后位置开始,所有数据前挪一单位,但是在挪之前,需要检查是否还存在数据;
官方文档定义其返回值为删除数据的下一位置。

十、迭代器在什么时候会失效?为什么要用这个迭代器?就是这个迭代器的作用是什么?vector有迭代器吗?

【注】指针一定是迭代器,但是迭代器不一定是指针。

vector在push_back的时候当容量不足时会触发扩容,导致整个vector重新申请内存,并且将原有的数据复制到新的内存中,并将原有内存释放,这自然是会导致迭代器失效的,因为迭代器所指的内存都已经被释放。

insert导致的迭代器失效的情况:
(1)插入操作导致vector扩容,迭代器失效原因和push_back相同;
(2)插入操作引起vector元素移动,导致被移动部分的迭代器失效;
(3)删除操作引起vector元素移动,导致被移动部分的迭代器失效。

十一、map和vector的应用场景?

如果你需要高效的随机存取,而不在乎插入和删除的效率,使用vector
如果你要存储一个数据字典,并要求方便地根据key找value,那么map是较好的选择;

如果你需要大量的插入和删除,而不关心随机存取,则应使用list
如果你需要随机存取,而且关心两端数据的插入和删除,则应使用deque
如果你要查找一个元素是否在某集合内存中,则使用set存储这个集合比较好。

十二、动态库中导出类和导出函数的区别?

只需要在class后加上__declspec(dllexport)即可实现导出类

在DLL中有一张导出表,其中有一系列函数,这些函数叫做导出函数。这些函数可供外部程序调用,即这些函数都是该DLL的入口点(类似main函数)。
不在导出表中的函数,为该DLL私有的函数,外部程序不能调用它们。

【注】没有__declspec(dllexport),将生成的测试lib库添加到项目中,直接调用,会报错。

十三、关于内存泄漏

出现内存泄漏的原因:一个是忘了释放,一个是重复释放

智能指针一定不会造成内存泄漏吗?使用的时候要注意什么?
两个智能指针相互指向,就会循环,造成内存泄漏。

十四、关于类的一个问题

有一个类A,其中有一个函数,函数功能只是打印“hello world”,如果:
A p = new A();
之后delete了这个p
然后使用p指向这个函数会发生什么?
可以正常打印“hello world”,
因为函数不属于对象,但是如果这个函数中用到了成员变量就会出错。
如果直接定义一个p = nullptr的指针,指向这个函数,也可以执行。
delete之后,sizeof这个对象是多大?1,要在内存中分配初始化所需的空间位置,为1。

如果在这个函数前面加上virtual,还是用这个p指向这个函数会发生什么?
访问出错,对象没了,虚函数表没了,访问不到了。
这个时候sizeof这个对象是多大?4,因为virtual有虚函数表,类中会有虚函数指针,指向这个表,即指针大小4。

十五、QT中的信号与槽的一些优点?

QT是基于消息触发机制实现的很多功能。

激发信号的Qt对象无须知道是哪个对象的哪个槽需要接收它发出的信号,它只需要做的是在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道哪个对象的哪个槽接收到了信号。
同样地,对象的槽也不知道是哪些信号关联了自己,而一旦关联信号和槽,Qt就保证了适合的槽得到了调用。
即使关联的对象在运行时被删除。应用程序也不会崩溃。

十六、怎么解决粘包问题?

1.利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题(因为只有一种包结构,类似于http协议)。
2.一般可能会在头加一个数据长度之类的包,以确保接收。

互联网学习 文章被收录于专栏

互联网知识点

全部评论
这两个区别讲的非常清楚
1 回复 分享
发布于 2022-08-19 14:50 陕西

相关推荐

05-26 22:25
门头沟学院 Java
Java小肖:不会是想叫你过去把你打一顿吧,哈哈哈
点赞 评论 收藏
分享
评论
5
49
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务