面试汇总

一.指针和数组的区别

1.数组:是一系列相同数据的集合,是开辟连续的内存空间,sizeof是数组占用内存的大小,而并非是数组中元素的个数。

2.指针是地址,存放变量的地址,sizoef占用的内存大小和操作系统有关,32位是占用4个字节,64位是占用8个字节。

二.使用const的好处

提高代码安全性。提高代码的可读性和可维护性。提高程序的效率。

三、指针和引用的区别

相同点:

1.都是地址的概念;

2.指针指向一块内存,它的内容是所指内存的地址;

3.引用是某块内存的别名。

不同点:

1.指针是一个变量,存储的是变量的地址,引用是给变量起一个别名。

2.指针可以为空,引用一经定义必须初始化。

3.指针可以改变指向,但是引用初始化之后不可以改变指向。

4.指针可以有多级操作,但是引用只有一级。

5.sizeof指针得到的是本指针的大小,但是引用得到是引用所指向变量的大小。

6.不存在指向空值的引用,但是有指向空值的指针。

四、对于static关键字的用法

对于函数内部,当不用static关键字修饰的时候,它是存放在栈区,当函数调用结束后,局部变量就销毁了。

而当使用static修饰了函数中变量,它存放在静态存储区,当整个程序执行期间,它分配的空间一直存在,只不过外部是无法访问的。这与全局区

使用static修饰的全局变量不同,他是本文件可以进行访问,同样,在整个程序期间的所分配的控件都存在。

五、函数重载

函数重载的三个条件,相同函数名的参数个数不同、参数类型不同、参数顺序不同满足其中一个即可。

那么当函数返回值类型不同可以作为函数重载,答案是不同。但是函数重载可以是返回值类型不同。

原理:c++编译器在链接的时候,找到汇编之后生成.o文件里面的符号表,进行链接,main函数能通过函数传参的不同找到它需要调用函数的地址。

六、虚函数的实现原理

虚函数的用法:可以让派生类重写基类的成员函数实现多态。虚函数实现多态的机制,严格来说是动态多态,是在运行期间实现的。

虚函数的实现原理:每个虚函数都会有一个与之对应的虚函数表,该虚函数表的实质是一个指针数组,存放的是每一个对象的虚函数入口地址。对于一个派生类来说,它会继承基类的虚函数表,同时增加自己的虚函数入口地址,如果派生类重写了基类的虚函数的话,那么会继承过来的虚函数入口地址将被派生类的重写函数入口地址代替。那么在程序运行时发生动态绑定,将父类绑定到实例化的对象实现多态。

七、析构函数可以是虚函数吗?

可以

当基类的指针指向派生类对象,如果delete掉该指针,那么就会调用该指针指向的派生类析构函数,然后派生类析构函数又自动调用析构函数,这样派生类的对象才被完全释放干净。如果不声明虚析构函数,会导致派生类对象释放不完全。

八、构造函数可以是虚函数吗?

不可以

因为调用构造函数之后才可以生成一个对象,假设构造函数是虚函数,因为虚函数存在虚函数表中,但是虚函数表又需要虚函数表指针去找,但是虚函数指针是存放在对象中的,这就前后矛盾.

九、宏和内联函数的区别

1.宏定义不是函数,但是使用起来像函数,宏是在预处理阶段的。内联函数是在编译阶段的。

2.内联函数里面不允许有开关语句、控制语句和循环语句。

3.宏相当于是直接替换,是没有类型检查的。但是内联函数是在编译阶段进行类型检查的。

十、new和malloc的区别

new的定义:new时运算符,可以用于动态分配。如果想要撤销内存使用delete。new运算符使用的一般格式为new类型,

用new分配数组空间时不能指定初值,new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。一般来说,使用

new申请空间,申请得到的空间的位置时根据当时的内存的实际使用情况所来决定。但是,在某些特定情况下,可能需要程序员指定的特定

内存创建对象。

malloc的定义:顾名思义就是动态分配内存,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,当无法知道内存具体

位置的时候,想要绑定真正的内存空间,就需要动态的分配内存。

1:属性方面:

new是关键字,需要编译器支持。malloc是库函数,需要头文件支持。

2:参数方面:

new申请内存无需指定内存的大小,编译器会根据信息自动计算。除此之外,new会自动调用c++中的构造函数。

malloc必须指定分配内存的大小,需要显示指出所需内存的尺寸,并且返回后强行转换为实际类型的指针。

并且malloc只管分配内存,所以得到的一片新内存是随机的。

3:处理数组方面:

int* p = new int[10];

new有处理数组觉得new[],使用new[]分配的内存必须使用delete[]释放。

malloc要想动态分配数组的内存大小,需要手动指定。

int* p = (int)malloc(sizeof(int)100);

4:返回类型:

new分配成功返回的是对象类型指针,与对象类型匹配,无类型转换,所以new是符合类型安全性操作的

malloc分配成功后返回值类型是void*,一般需要强制类型转换。

5:分配失败方面:

new内存分配失败的时候,抛出的是bad_balloc异常。malloc分配内存失败方面返回的是NULL.

6:自定义类型方面

new会先调用operator new函数,申请足够的内存,然后调用构造函数,初始化成员变量,最后返回自定义类型指针,delete先调用析构函数。

malloc是库函数,只能动态的申请和释放内存,无法强制要求做自定义类型对象的构造函数和析构函数。

7:重载方面:

new 可以重载,malloc不可以重载。

8:内存区域方面:

new为自由存储区域,malloc在堆上分配。

9:效率方面:

new的效率高于malloc

十一:四大强制类型转换

1:static_cast

static_cast是”静态转换“的意思。

int m = 10;

doubel  n = static_cast<double>(int);

int* p = static_cast<int*>(malloc(sizeof(int)*10));//将void指针转换为一般类型指针

void* pp = static_cast<void*>(p);//将具体指针转换成void*

2: reinterpret_cast

用于各种不同类型的指针指针,不同类型的引用之间,以及指针和容纳指针的整数类型之间的转换。

3:const_cast

const_cast运算符用来去除const属性操作的转换。将const引用转换成非const引用,将const指针转换成同类型

的非const指针。

4:dynamic_cast

dynamic_cast是专门用来将基类指针或引用强制转换成派生类的指针或引用,而且可以检查转换的安全性。对于不安全的

指针转换,转换结果返回NULL指针。

dynamic_cast允许用域在类的继承之间进行类型转换,它既允许向上转型,向上转型是不会做任何检测,所以都能成功。但是向下

转换的前提必须是安全的。

十二、void*指针

void*指针表明该指针与一地址相关。有几种限制:

可以当作函数参数传递void*指针

可以返回函数void*指针

可以给另一个void*指针赋值。

但是不可以使用void*指针操纵其指向的对象

十三、const char* 和指针之间的区别

const char* pp = "lihhongbing";

int offset = 2;
const char* buff = pp + offset;//ihhongbing

cout << *(pp + 1);//i

 十四、两个字符串数组之间比较使用strcmp函数

char str[] = "hello";

char str1[] = "hello";

使用等于号比较是否相等会失效,因为它比较的是str和str1的地址。

所以利用strcmp函数来以此进行比较。

十五、sizeof和strlen函数的区别

sizeof求字符串长度总是加一个结束符。

strlen求字符串长度是以null结尾。

char str[] = "hello";

sizeof(str):6

strlen(str):5

char str1[10] = {'a','b','c','\0'};

sizeof(str1):10*类型(10*1)

strlen(str1):3

十六、struct和class的区别

struct默认访问方式是public,class默认访问方式是private

struct默认继承方式是public,class默认继承方式private

十七、拷贝构造函数的形参为什么必须要用引用

原因:因为拷贝构造函数得参数不是一个引用,那么就相当于是采用了传值得方式,而传值得方式会调用该类的

拷贝构造函数,从而造成无穷无尽得递归调用拷贝构造函数。

十八、操作符不能重载

::    .*   .   ?:

注意:重载操作符必须具有至少一个类类型或枚举类型的操作数。这条规则强制重载操作符不能重新

定义用于内置类型对象的操作符的含义。

十九、public、protected和private三种继承方式

只有共有继承才可以继承基类中的接口,这种方式称之为接口继承。凡是protected或private继承的方式,都不可以继承基类的

接口,这种方式称之为实现继承。

二十、当使用多态的时候,构造函数的顺序是 先基类后父类。但是析构函数不一定。如果不定义成虚析构,也就是在析构函数前面加上

virtual关键字。那么析构是只会析构掉基类的析构函数,并不会析构派生类析构函数,所以防止这种情况。在使用多态时,析构函数一律加上

virtual关键字。

二十一、重载,重写和覆盖的区别

重载:

1.是在(相同范范围内)同一作用域中

2.函数名必须相同,参数类型、参数个数、顺序可以不同。

3.返回值类型可以不同。但是返回值不同的函数并不能说明是重载函数。

4.virtual关键字可有可无。

重写(覆盖)

1.是在(不同范围内)基类和派生类之间

2.函数名字和参数个数,参数列表必须相同。

3.返回值类型必须相同。

4.基类函数必须有virtual关键字。

5.访问修饰符可以不同。尽管virtual是private的,派生类中重写改写成public,protected也是可以的。

隐藏 :

1.是在(不同范围内)基类和派生类之间

3.返回值类型可以不同。

4.函数名必须相同。

5.参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)而不是被重写。

6.参数相同,但是基类函数有无virtual关键字都会被隐藏。此时,基类的函数被隐藏(注意与覆盖容易混淆)。

二十二、派生类不能继承基类中的有构造函数,析构函数,友元关系。

二十三、解释using声明和using指示之间的区别

using 声明:每次只引入命名空间的一个成员(using xxx::memeber)。其有效范围从 using 声明的地方开始,一直到 using 声明所在的作用域结束为止。在此过程中,外层作用域的同名实体将被隐藏。

using 指示:以关键字 using 开始,加上 namespace 以及命名空间的名字(using namespace xxx)。using 指示使得某个特定的命名空间中所有的名字都可见,这样我们就无须再为这些名字添加任何前缀限定符了。简写的名字从 using 指示开始,一直到 using 指示所在的作用域结束都能使用。

使用 using 指示存在的风险:

全局命名空间污染。如果应用程序使用了多个不同的库,通过使用 using 指示,命名空间中所有成员的名字都变得可见,这会导致二义性,也就是全局命名空间污染问题。
using 指示引发的二义性错误只有在使用了冲突名字的地方才能被发现。这意味着可能在引入某个库很久之后才会爆发冲突
相比于使用 using 指示,在程序中对命名空间的每个成员分别使用 using 声明效果更好,这样做一是可以减少注入到命名空间的名字数量,二是 using 声明引起的二义性问题在声明处就能发现,无须等到使用名字的地方,这对检测并修改错误有益。
二十四、数据库中图和表的区别

1.视图是已经编译好的sql语句,表不是。

2.视图没有实际的物理记录,表有。

3.表是内容,视图是窗口。

4.表占用物理空间,视图不占物理空间。

5.表是概念模式,视图是外模式。

6.表属于全局模式中的表,视图属于局部模式。

二十五、strcpy,memcpy,sprintf三者的区别。

1.strcpy字符串,不需要指定长度,它遇到被复制字符的串结束符 '\0’才结束

2.memcpy可适用于任意数据类型,完整复制num个字节。

3.sprintf:目的对象是字符串,源对象可以是任意字符串,也可以是任意基本类型的数据对象。‘

二十六、数据库中事务的概念

1.事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全部做,是一个不可分割的工作单位。

2.事务和程序是两个不同的概念:在关系库数据库中一个事务可以是一条SQL语句,一组SQL语句或整个程序。一个应用程序通常是包含多个事务。

3.事务是恢复和并发控制的基本单位。

事务的特性:

原子性:事务包括的操作要么全做,要么全不做。

一致性:事务执行的结果必须是使数据库从一个一致特性转变到另一个一致特性。

隔离性:一个事务的执行不能被其他事务干扰,而影响它对数据的正确使用和修改。

持久性:一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,接下来的其他操作或故障就不应该对其执行结果有任何影响。

 二十七、qt中信号和槽的同步和异步

1.Qt::DirectConnection(直连方式) 信号和槽函数类似于函数调用同步运行。

2.Qt::QueuedConnection(排队方式)此时信号被塞到信号队列里了,信号和槽函数类似于消息通道,异步运行。

当信号发出后,排队到信号队列中,需要等到接收对象所属线程的事件循环取得控制权时才取得该信号。

调用对应的槽函数。emit语句后得代码将在发出信号后马上运行。

3.Qt::AutoConnection(自己主动方式)

Qt默认连接方式,假设信号得发出和接收这个信号和对象同属一个线程,那个工作方式和直连方式同样。

4.Qt::BlockingQueuedConnection

信号和槽必须在不同得线程中。否则就会产生死锁。

这个是全然同步队列仅仅有槽线程运行完毕才会返回。否则发送线程也会一直等待,相当于是不同的线程能够同步起来运行。

5.Qt::UniqueConnection

于默认工作方式同样。仅仅是不能反复连接同样的信号和槽。由于假设反复连接就会导致一个信号发出。相应槽函数就会运行多次。

6.Qt::AutoCompatConnection

是为了连接Qt4与Qt3的信号槽机制兼容方式。工作方式与Qt::AutoConnection一样。

二十八、多重继承下,基类的构造顺序总是先虚基类构造函数,非虚基类构造函数,派生来构造函数。

二十九、当一个类直接继承多个基类的时候,那些类有可能本身还共享另一个基类,在这种情况下,可以使用将中间类设定为虚继承。

三十、Tcp和Udp的区别

Tcp是面向连接的,Udp面向无连接的。

Tcp提供可靠的服务,Udp提供不可靠的服务。

Udp具有较好的实时性,工作效率比Tcp高,适用于高速传输和实时性有较高的通信或广播通信。

Tcp只能是一对一的,而Udp可以是一对一,一对多,多对一和多对多。

Udp分组首部开销小,Tcp首部开销是20个字节,Udp只有首部只有8个字节。

Tcp是面向字节流。udp是面向报文的一次的完整的报文,报文是不可分割的。

报文是Udp数据处理的最小单位。

posted @   代码改变世界—LHB  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示