C++面试问题总结

一、static的作用
不考虑类,static的作用主要有三条。
1、第一个作用:隐藏
当我们同时编译多个文件时,所有未加static前缀的全局变量函数都具有全局可见性。
解释:假设我们要同时编译两个源文件,一个是a.cpp,另一个是main.cpp
a.cpp如下:

#include<iostream>
char a='A';
void msg(){
	cout<<"hello"<<endl;
}

则a.cpp中定义的全局变量a和函数msg能在main.cpp中使用。
如果加了static,就会对其他源文件隐藏。例如在a和msg的定义前加上static,main.cpp就看不到他们了。
那么利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心名字冲突。
2、第二个作用:默认初始化为0
包括未初始化的全局静态变量和局部静态变量。
其实未初始化的全局变量也具备这一点,因为未初始化的全局变量和未初始化的静态变量是存储在同一块区域内的(BBS段);在BBS段中,内存中所有的字节默认值都是0x00
3、第三个作用:保持局部变量内容的持久
函数内的局部变量,当在调用时就是存在,退出函数就消失;
但静态局部变量虽然在函数内定义,但静态局部变量始终存在着,也就是说它的生存期是整个源程序,其特点是只进行一次初始化且具有“记忆性”。
静态局部变量的生存期虽然为整个源程序,但是其作用域仍与局部变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后,尽管该变量还继续存在,但不能使用它。

考虑类,类中的static的作用。
表示属于一个类而不属于此类的任何特定对象的变量和函数。
static成员可以是函数或数据,都独立于类类型而存在。
1、静态数据成员
①非static数据成员存在于类类型的每个对象中;static数据成员独立于该类的任意对象而存在。(也就是说,静态成员是类的,只有一份,被每个对象共享,可以直接用类名调用,也可以被类对象调用,但通过类对象调用的依然是共享的数据成员)
②static数据成员必须在类定义体的外部定义(正好一次);静态数据成员也存储在全局(静态)存储区,静态数据成员定义时要分配空间,所以不能在类声明中定义。
③类的static成员,像普通数据成员一样,不能在类的定义体中初始化,static数据成员通常在类定义体的外部定义时才初始化。(即在类定义体中对静态变量赋初值是错误的

2、静态成员函数
①静态成员之间可以相互访问,包括静态函数访问静态数据成员和访问静态成员函数。
静态成员函数不能访问非静态成员函数和非静态数据成员,但非静态成员函数可以任意的访问静态成员函数和静态数据成员。
②(因为普通成员函数总是具体的属于某一类的具体对象,所以普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身。但是静态成员函数不与任何的对象相关联,因此不具有this指针)由于没有this指针的额外开销,因此静态成员函数与类的非静态成员函数相比速度上会有少许的增长。

二、const的作用
1、常量
const限定符把一个对象转换成一个常量;因为常量在定义的时候就不能修改,所以定义时必须初始化。
在全局作用域定义的非const变量,在整个程序中都可以访问;但是const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。但是通过指定const 变为extern,就可以在整个程序中访问const对象。
2、指针和const修饰符
指向const的指针和const指针:
double* ptr=&value;//ptr是一个指向double类型的指针,ptr的值可以改变,ptr所指向的value的值也可以改变;
const double* ptr=&value;//ptr是一个指向const double类型的指针,ptr的值可以改变,不能通过ptr改变value的值;
double* const ptr=&value;//ptr是一个指向double类型的 const 指针,ptr的值不可以改变,可以通过ptr改变value的值;
const double* const ptr=&value;//ptr是一个指向const double类型的 const 指针,ptr的值不可以改变,也不能通过ptr改变value的值;
3、修饰函数参数和返回值
①const修饰返回值
适当情况下使用const修饰的返回值,从而可以避免错误使用导致运行时的崩溃
②const用来修饰函数的参数

4、const在类中的应用
const实施于成员函数的目的:是为了确保成员函数可作用于const对象身上。
const对象、指向const对象的指针或者引用只能调用其const成员函数;
而非const对象可调用非const成员函数与const成员函数。
显然,若不存在const成员函数,那么const对象的操作就极其困难,无法调用任何成员函数;
因此实现const成员函数,使得可以对const对象产生操作。

三、C++中,static、const以及static const成员变量的初始化
①在C++中,static静态变量不能在类的内部初始化,在类的内部只能是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,static关键字只能用于类定义体内部的声明中,定义时不能标示为static。
②在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。
const数据成员只在某个对象生存期内是常量,而对于整个类而言确实可变的。
因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

四、strlen、strcmp、strcat、strcpy、memcpy、memset、sizeof、union

五、指针与引用都可间接访问另一个值的,他们的区别
1、引用不能为空,当引用被创建时,它必须被初始化。而指针可以为空值,可以在任何时候被初始化。
2、一旦一个引用被初始化为指向一个对象,它就不能被改变为另一个对象的应。而指针则可以在任何时候指向另一个对象。
3、不可能有NULL引用。必须确保引用是和一块合法的存储单元关联。
4、sizeof(引用)得到的是所指向的变量(对象)的大小。而sizeof(指针)得到的是指针本身的大小
5、如果返回动态分配的对象或内存,必须使用指针,引用可能会有内存泄露
6、引用使用的时候不用解引用,而指针需要解引用,引用和指针的自增(++)操作的运算意义不一样(引用是对值的自增,指针是对内存地址的自增)
typedef:用来说明一种新的类型名,来代替已有的类型名
typedef并未产生新的数据类型,它的作用仅仅是给已已存在的类型名起一个“别名”,且原有的类型名依然有效。
void 指针*:一种特殊的指针类型,它可以保存任何类型对象的地址。

六、内联函数、宏定义和普通函数的区别
①内联函数要做参数类型检查,这是内联函数跟宏相比的优势
②宏定义是在预编译 的时候把所有的宏名用宏体替换,简单的说就是字符串替换
内联函数则是在编译的时候进行代码插入,编译器会在每处调用内联函数的地方直接 把内函数的内容展开,这样可以省去函数的调用的压栈出栈的开销,提高效率
③内联函数是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于短小简单的代码来说,内联函数可以带来一定的效率提升,而且和C时代的宏函数相比,内联函数更安全更可靠。但是这个是以增加空间消耗为代价的。
④const与#define的区别:宏在预处理阶段替换,const在编译阶段替换;
宏没有类型,不做安全检查;const有类型,在编译阶段进行安全检查。

七、C++中类与结构体的区别
①最本质的一个区别就是默认的访问控制:
struct作为数据结构的实现体,它默认的数据访问控制是public
class作为对象的实现体,它默认的成员变量访问控制是private
②class这个关键字还用于定义模板参数,就像"typename"。但是关键字struct不用于定义模板参数

八、Volatite关键字的主要作用
参考此处
基本理解而言:被Volatile修饰的共享变量,就具有了以下两点特性:
1、保证了不同线程对该变量操作的内存可见性
2、禁止指令重排序

内存可见性:当一个线程次改了某个状态对象后,其他线程能够看到发生的状态变化。
比如线程1修改了变量A的值,线程2能立即读取到变量A的最新的值;
但是如果要保证内存可见性,必须要保证以下两点:
①线程修改后的共享变量值能够及时刷新从工作内存中刷新回主内存
②其他线程能够及时的把共享变量的值从主内存中更新到自己的工作内存中

九、重载与重写的区别
简单来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(参数个数不同,参数类型不同,返回值不同)
重写:是指子类重新定义父类虚函数的方法

十、模板的特例化
引入原因:编写单一的模板,它能适应大众化,使每种类型都具有相同的功能;
但是对于某种特定的类型,如果要实现其特有的功能,单一模板就不无法做到,这时就需要模板特例化;
定义:是对单一模板提供的一个特殊实例,它将一个或多个模板参数绑定到特定的类型或值上。

十一、explicit关键字的作用
用来防止由构造函数定义的隐式转换。(只能显式构造函数)
1、C++中的隐式构造函数
如果C++类的其中一个构造函数有一个参数,哪呢在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类的对象。
2、为了避免上面提到的只有一个参数的构造函数采用的缺省转换操作,在构造函数前,使用explicit关键字修饰即可。

十二、C++中的四种类型转换:
1、const_cast:去掉const或者volatile属性
常量转换,将const变量转换为非const,去掉volatile
2、static_cast:无条件转换,静态类型转换
①基本数据类型转换 int,char,float,struct之间的转换
②把任何类型的表达式转换为void类型
③基类和子类之间的转换:其中子类指针转换为父类指针是安全的;但是父类指针转换为子类指针是不安全的。
3、dynamic_cast:有条件转换,动态转换
①必须有虚函数
②安全的基类和子类之间的转换
③相同基类不同子类之间的交叉转换,但结果返回NULL
4、reinterpret_cast:仅重新解释类型,但没有进行二进制的转换
①最普通的用途就是在函数指针类型之间进行转换
②很难保证移植性

十三、计算机网络模型
参考这儿
1、OSI参考模型(开放系统互联网参考模型)
有七层,自下而上依次为物理层、数据链路层、网络层
传输层、会话层、表示层、应用层
2、TCP/IP模型
在这里插入图片描述
TCP/IP不是一个协议,而是一个协议族的统称,里面包括了IP协议、IMCP协议、TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等。电脑有了这些,就好像学会了外语一样,就可以和其他的计算机终端做自由的交流了。
**应用层:**向用户提供一组常用的应用程序,比如电子邮件,文件传输访问,远程登陆等
**传输层:**提供应用程序间通信。其功能包括:一、格式化信息流;二、
提供可靠传输。为实现后者,传输层协议规定接受端必须发回确认,并且假如分组丢失,必须重新发送。
**网络层:**负责相邻计算机之间的通信
**网络接口层:**这是最底层,负责接收IP数据报并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层
3、IP是无连接的
IP用于计算机之间的通信。它不会占用两个正在通信的计算机之间的通信线路。
这样IP就降低了对网络线路的需求。每条线可以同时满足许多不同的计算机之间的通信需要
通过IP,消息(或者其他数据)被分割为小的独立的包,并通过因特网在计算机之间传送。
IP负责把每个包路由至目的地
4、IP地址
每个计算机必须有一个IP地址才能够连入因特网
每个IP包必须有一个地址才能够发送到另一台计算机
5、TCP使用固定的连接
TCP用于应用程序之间的通信
当应用程序希望通过TCP与另一个应用程序通信时,它会发送一个通信请求。
这个请求必须被送到一个确切的地址。
在双方“握手”之后,TCP将在两个应用程序之间建立一个双全工的通信
这个双全工的通信将占用两个计算机之间的通信线路,直到它被一方或者双方关闭为止
6、IP路由器
当一个IP包从一台计算机被发送,它会到达一个IP路由器。
IP路由器负责将这个包路由至它的目的地,直接地或者通过其他的路由器。
7、域名
12个阿拉伯数字很难记忆。使用一个名称更容易。
用于TCP/IP地址的名字被称为域名。
DNS服务器负责将域名翻译为TCP/IP地址,同时负责使用新的域名信息更新彼此的系统。
当一个新的域名连同TCP/IP地址一同注册后,全世界的DNS服务器都会对此信息进行更新。
8、TCP/IP
TCP/IP 意味着TCP/IP在一起协同工作
TCP负责应用软件(比如你的浏览器)和网络软件之间的通信。
IP负责计算机之间通信
TCP负责将数据分割并装入IP包,然后在它们到达的时候重新组合他们
IP负责将包发送至接收者
9、TCP三次握手
所谓三次握手即建立TCP连接;
就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。
ACK:确认标志。表示确认编号栏有效。
SYN:同步标志。表示同步序列编号栏有效。该标志仅在三次握手建立TCP连接时有效。
简单来说:
①:建立连接时,客户端发送SYN包到服务器,并进入SYN-SEND状态,等待服务器确认。
②:服务器收到SYN包,必须确认客户的SYN,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN-RECV状态。
③:客户端收到服务器SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务端进入established状态,完成三次握手,客户端和服务端开始传送数据。
10、TCP四次挥手
所谓四次挥手即终止TCP连接,就是断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。
①第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
②第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
③第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
④第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
11、为什么建立连接时三次握手,而关闭连接却是四次挥手呢?
因为服务端在Listen状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
然而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了,(但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接)
因此客户端ACK和FIN 一般都会分开发送。
十四、操作系统基础
1、进程
程序本身只是指令的集合,进程才是程序的真正运行。
用户下达运行程序的命令后,就会产生进程。
同一程序可产生多个进程(一对多的关系),以允许同时有多位用户运行同一程序,却不发生冲突。任何时间仅能运行一项进程。
通常进程有5种状态,前三种是基本状态。
① 运行状态:进程正在处理器上运行。每一时刻最多只有一个进程处于运行状态。
② 就绪状态:进程已处于准备运行的状态,即进程获得了除处理器之外的一切资源,一旦得到处理器即可运行
③ 阻塞状态:又称为等待状态,进程正在等待某一事件而暂停运行
④创建状态:进程正在被创建,尚未转到就绪状态。
⑤ 结束状态:进程正从系统中消失。
就绪变成执行:当一个就绪进程获得处理时机时
执行变成就绪:当一个进程被剥夺处理时机时(如用完系统分配给它的时间片,或者出现更高优先级别的其他进程)
执行变成阻塞:当一个进程因某件事受阻时(如申请的内存被占用、启用I/O传输未完成)
阻塞变成就绪:当所等待的事件发生时(如申请到资源、I/O传输完成)
2、线程
有时被称为轻量级进程,是程序执行流的最小单元
线程由线程ID、当前指令指针、寄存器集合和堆栈组成
线程是进程中的一个实体,它不占用系统资源,只拥有一点在运行中必不可少的资源,可与同属于一个进程的其他线程共享进程所拥有的资源
3、进程与程序的区别
①进程是程序及其数据在计算机上的一次运行活动,是一个动态的概念。进程的运行实体是程序、离开程序的进程没有意义。而程序是一组有序的指令集和,是一种静态的集合
②进程是程序的一次执行的过程,它是动态的创建和消亡的,具有一定的生命周期,是暂时存在的;
而程序则是一组代码的集合,它是永久存在的,可长期保存。
③一个进程可以执行一个或几个程序,一个程序也可以构成多个进程
④进程与程序的组成不同;进程的组成包括程序,数据和进程控制块
4、进程通信
多个进程可以共享系统中的各种资源,但其中许多资源一次只能为一个进程使用,我们把一次仅允许一个进程使用的资源成为临界资源。
5、死锁
定义:是指多个进程因竞争资源而造成的一种僵局(互相等待)若无外力的作用,这些进程都将无法向前推进。
现实生活的例子:交通阻塞,两股相向而行的车流都想通过己被对方占用的道路,结果双方都不能前进。
产生死锁的必要条件:(只要其中任一个条件不成立,死锁就不会发生)
① 互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占用。此时若有其他进程请求该资源,则请求进程只能等待。
② 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放。
③请求和保持条件:
④循环等待条件:
死锁处理策略:
①预防死锁:设置某些限制条件,破坏产生死锁的四个必要条件中的一个或几个
②避免死锁:在资源的动态分配过程中,用某种方法防止系统进入不安全状态。银行家算法是著名的死锁避免算法;
③死锁的检测及解除

十五、内存管理
1、内存管理:操作系统对内存的划分和动态分配
内存管理的功能有:
①内存空间的分配与回收,包括内存的管理与共享
②地址转换,把逻辑地址转换成相应的物理地址
③内存空间的扩充,利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存
④存储保护,保证各道作业在存储空间内运行,互不干扰
2、内存分配管理方式
包括连续分配方式 ,非连续分配管理方式
其中非连续分配管理方式允许一个程序分散的装入到不相邻的内存分区中,根据分区的大小是否固定分为分页存储管理方式和分段存储管理方式。
分页存储管理方式中又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页存储管理方式和请求分页存储管理方式。
3、基本分页存储管理方式
①固定分区会产生内部碎片,动态分区会产生外部碎片,这两种技术对内存的利用率都比较低。我们希望内存的使用能尽量避免碎片的产生,这就引入了分页的思想:
把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。
每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
②分页的方法从形式上看,像分区相等的固定分区技术,分页管理不会产生外部碎片。但它又有本质的不同点:块的大小相对分区要小很多,而且进程也按照块进行划分,进程运行时按申请主存可用空间并执行。这样只会在为最后一个不完整的申请一个主存空间时,才会产生主存碎片,所以尽管会产生内部碎片,但是这种碎片相对进程来说也是很小的,每个进程平均只产生半个块大小的内部碎片(也称页内碎片)

十六、数据结构部分

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值