c++ 面试题 面试题复盘

Posted codeandlearn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++ 面试题 面试题复盘相关的知识,希望对你有一定的参考价值。

1. #include<filename.h>与#include"filename.h"的区别

对于#include <filename.h> ,编译器从标准库路径开始搜索filename.h,
对于#include “filename.h” ,编译器从用户的工作路径开始搜索filename.h
# 2 C++中的类与c中的struct的区别
c++ 中的struct与 c中的struct区别

a) C语言中的结构体不能为空,否则会报错

b) C语言中的结构体只涉及到数据结构,而不涉及到算法,也就是说在C中数据结构和算法是分离的。换句话说就是C语言中的结构体只能定义成员变量,但是不能定义成员函数。然而在C++中既可以定义成员变量又可以定义成员函数, C++中的结构体和类体现了数据结构和算法的结合

c) C语言的结构体中不能定义成员函数,但是却可以定义函数指针,不过函数指针本质上不是函数而是指针,所以总的来说C语言中的结构体只是一个复杂数据类型?,只能定义成员变量,不能定义成员函数,不能用于面向对象编程

d) 在C语言中结构体变量定义的时候,若为struct 结构体名 变量名定义的时候,struct不能省略。但是在C++之中则可以省略struct。

c++ 中的类与 c++中的struct区别
概念:class和struct的语法基本相同,从声明到使用,都很相似,但是struct的约束要比class多,理论上,struct能做到的class都能做到,但class能做到的stuct却不一定做的到
类型struct是值类型,class是引用类型,因此它们具有所有值类型和引用类型之间的差异
效率:由于堆栈的执行效率要比堆的执行效率高,但是堆栈资源却很有限,不适合处理逻辑复杂的大对象,因此struct常用来处理作为基类型对待的小对象,而class来处理某个商业逻辑。
关系struct不仅能继承也能被继承 ,而且可以实现接口,不过Class可以完全扩展。内部结构有区别,struct只能添加带参的构造函数,不能使用abstract和protected等修饰符,不能初始化实例字段

3.malloc/free 与 new delete

1.malloc/free为C的标准库函数,函数原型为:

void* malloc(size_t size)//参数代表字节个数
void free(void* pointer)//参数代表内存地址

new 和delete是运算符
new、delete则为C++的操作运算符,它调用的分别为赋值运算符重载operator new()和operator delete();

  1. malloc/free的使用如下
//用malloc分别开辟了1个和4个整型大小的空间和并free释放它们;
void func() 
{ 
//开辟一个空间
int* p1=(int*)malloc(sizeof(int));
if(p1==NULL)
{
exit(1);
}
free(p1);
//开辟多个空间
int*p2=(int*)malloc(sizeof(int)*4);
if(p2==NULL)
{
exit(1);
}
free(p2);
}

new/delete如下:

void func() 
{
//开辟一个空间
int* p1=new int(1);
delete p1;
//开辟多个空间
int*p2=new int[4];
delete []p2;
}

由上可知

1)malloc开辟空间类型大小需手动计算,new是由编译器自己计算;?
(2)malloc返回类型为void*,必须强制类型转换对应类型指针,new则直接返回对应类型指针;?
(3)malloc开辟内存时返回内存地址要检查判空,因为若它可能开辟失败会返回NULL;new则不用判断,因为内存分配失败时,它会抛出异常bac_alloc,可以使用异常机制;?
(4)无论释放几个空间大小,free只传递指针多个对象时delete需加[](原因在第3);
(5)malloc/free为函数只是开辟空间并释放,new/delete则不仅会开辟空间,并调用构造函数和析构函数进行初始化和清理,如下为new/delete、new[]/delete[]实现机制
技术图片
技术图片

即过程如上,在开辟大小会多开辟四个字节,用于存放对象的个数,在返回地址时则会向后偏移4个字节,而在delete时则会查看内存上对象个数,从而根据个数count确定调用几次析构函数,从而完全清理所有对象占用内存。

4.由上图还可以看出new/delete底层是基于malloc/free来实现的,而malloc/free不能基于new/delete实现
5.因为new/delete是操作符,它调用operator new / operator delete,它们可以被重载,在标准库里它有8个重载版本;而malloc/free不可以重载;
6.对于malloc分配内存后,若在使用过程中内存分配不够或太多,这时可以使用realloc函数对其进行扩充或缩小,但是new分配好的内存不能这样被直观简单的改变
7.对于new/delete若内存分配失败,用户可以指定处理函数或重新制定分配器(new_handler(可以在此处进行扩展)),malloc/free用户是不可以处理的。
8.最后一点对于new/delete与malloc/free申请内存位置说明,malloc我们知道它是在堆上分配内存的,但new其实不能说是在堆上,C++中,对new申请内存位置有一个抽象概念,它为自由存储区,它可以在堆上,也可以在静态存储区上分配,这主要取决于operator new实现细节,取决与它在哪里为对象分配空间。

# 4 类的多态.封装,继承的理解
从实现角度分析这个问题
封装
抽象与具体实现的封装,隐藏数据的封装,函数定义与声明的封装。
1.将类的公有接口与实现细节分开,公有接口表示的是设计的抽象组件,而将类的实现细节放在一起并将它们与抽象组件分开是一种封装。将数据隐藏在类的私有部分是一种封装,将实现细节放在私有部分是一种封装,将类的函数定义和声明放在不同文件中也是一种封装。
继承
继承可以说是一种代码复用的手段,我们在一个现有类上想扩展出一些东西的时候,不需要再次重复编写上面的代码,而是采用一种继承的思想。在派生出的子类里添加一些我们想要的数据或方法,也可以理解为从一般到特殊的过程。
继承使用
相比与函数,类提供了数据的表示方法以及类的使用方法,通常类库以源代码的形式形式提供,通过修改类的内容可以满足需求。但是继承使得派生类直接可以从基类获取其特征,也可以1. 可以再已有类的基础上添加新的功能。2. 可以给类添加数据。3. 可以修改类的方法。
总结

多态
先说运行时多态,其实是指在继承体系中父类的一个接口(必须为虚函数),在子类中有多种不同的实现,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。即通过父类指针或者引用可以访问到子类的接口(虚函数),看上去就像是一个相同的动作,会出现多种不同的结果。

# 5. 编写程序判断 bool float int 和0的关系
bool型变量

if(flag)
if(!flag)//其中flag为bool类型。

int变量

if(value==0)
if(value!=0),其中value为整型变量

浮点(float和double)变量

const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)

指针类型变量

if(p==NULL)
if(p!=NULL)

# 6 不使用c++ 或者c 的库函数编写strcopy函数的实现
需要深入理解内存的分配

#include <stdio.h>
#include <assert.h>

char *strcpy(char *strDest,const char *strSrc)
{
    assert((strDest!=NULL) && (strSrc!=NULL));

    if(strDest==strSrc)
        return strDest;

    char *address=strDest;
    while((*strDest++ = *strSrc++)!='')
        ;
    return address;
}

# 7 计算sizeof 的结果

char a[30];//定义了一个长度为30的字符串数组char
b = (char)malloc(20 * sizeof(char));//定义了一个指针b,它指向一个开辟了20长度的字符数组
printf("%d",sizeof(a));//这个结果是30,也就是上面说的字符数组的长度
printf("%d",sizeof(b));//这个结果是4,因为任何指针(无论指向什么)的本身长度都是4
printf("%d",sizeof(a[3]));//这个结果是1,因为a[3]就特指数组中的一个值,比如a[0],a[1],a[2]都是1
printf("%d",sizeof(b+3));//这个结果是4,因为b是指针,你将指针挪动了3位,它所指向的地方了//(也就是那个新开辟的20大小的数组)向后挪动了3位.//但是它终究还是个指针,所有指针长度都是4
printf("%d",sizeof(*(b+4)));//这个结果是1,因为b+4将指针后移4位,指向了那个20长度字符数组中//的第4位了,而字符数组中的每一位都是一个字符而已,每个字符占1位printf("%d",sizeof(u));//这个结果是8,我记得我上学的时候老师说这种结构体的大小就和里面最大//的元素的大小一样,那doule类型占8位,所以这个就是8了所以输出的结果应该是3041418

# 8 判断数组对象返回的函数指针,通过printf输出的结果

#include<iostream>
using namespace std;
char *GetMemory(void)
{
???????char p[] = "hello word";
???????return p;
}
void Test(void)
{
???????char* str = NULL;
???????str = GetMemory();
???????//printf("%s",str);
???????printf( str);
}
int main()
{
???????Test();
???????//因为p的生命周期在GetMemory函数执行完了就被销毁了,str 指向的是个野指针
}

# 9 使用的STL容器有哪些,用过的其中的算法有哪些、
1、顺序容器:是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。顺序容器包括:vector(向量)、list(列表)、deque(队列)。
? 2、关联容器:关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。元素是有序的集合,默认在插入的时候按升序排列。关联容器包括:map(集合)、set(映射)、multimap(多重集合)、multiset(多重映射)。
? 3、容器适配器:本质上,适配器是使一种不同的行为类似于另一事物的行为的一种机制容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作“它保存一个容器,这个容器再保存所有元素”。STL 中包含三种适配器:栈stack 、队列queue 和优先级队列priority_queue 。

? ? ? ?容器类自动申请和释放内存,因此无需new和delete操作。

# 10 如下算法从迭代器中消除2是否存在错误

#include<iostream>
#include<vector>
using namespace std;
typedef vector<int>IntArray;
int main()
{
???????IntArray array;
???????array.push_back(1);
???????array.push_back(2);
???????array.push_back(2);
???????array.push_back(4);
???????for (IntArray::iterator itor = array.begin(); itor != array.end(); itor++)
???????{
??????????????if (2 == *itor)
??????????????{
?????????????????????array.erase(itor);
?????????????????????//以上存在严重的错误,当erase itor后,itor变成了一个野指针,对于野指针进行操作明显是错误的。
??????????????}
???????}

以上是关于c++ 面试题 面试题复盘的主要内容,如果未能解决你的问题,请参考以下文章

面试4轮字节Java研发岗,最终拿下Offer(原题复盘)

金三银四华为软件测试一面,12个面试题复盘

你知道 Android 面试官最喜欢问那些技术点嘛?这篇带你复盘往年面试题!

记录一下阿里5面+HR面,我的面试题复盘总结+面试资料

前端面试题之手写promise

记一次C++后台开发面试拷打过程