c++.docx
- 文档编号:7445735
- 上传时间:2023-01-24
- 格式:DOCX
- 页数:18
- 大小:724.28KB
c++.docx
《c++.docx》由会员分享,可在线阅读,更多相关《c++.docx(18页珍藏版)》请在冰豆网上搜索。
c++
1.new、delete、malloc、free关系
delete会调用对象的析构函数,和new对应free只会释放内存,new调用
构造函数。
malloc与free是C++/C语言的标准库函数,new/delete是
C++的运算符。
它们都可用于申请动态内存和释放内存。
对于非内部数据类
型的对象而言,光用maloc/free无法满足动态对象的要求。
对象在创建的同
时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于
malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执
行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个
能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释
放内存工作的运算符delete。
注意new/delete不是库函数。
总结:
new和delete会自动调用对象的构造与析构函数而malloc与free不会;
new和delete式C++运算符,而malloc和free是C/C++标准库函数。
继承优缺点
类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。
但是类继承也有一些不足之处。
首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。
更糟的是,父类通常至少定义了子类的部分行为,父类的任
何改变都可能影响子类的行为。
如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。
这种依赖关系
限制了灵活性并最终限制了复用性
多态
多态性使得能够利用同一类(基类)类型的指针来引用不同类的对象,以及根据所引用对象的不同,以不同的方式执行相同的操作。
把不同
的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
赋值
之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(也就是可以调用子对象中对父对象的相关函数的改进方
法)
函数的联编:
在编译或运行将函数调用与相应的函数体连接在一起的过程。
1先期联编或静态联编:
在编译时就能进行函数联编称为先期联编或静态联编。
2迟后联编或动态联编:
在运行时才能进行的联编称为迟后联编或动态联编。
虚函数的原理:
当编译器遇到virtual后,会为所在的类构造一个表和
一个指针,那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址,我们可以把vtbl形象地看成一个数
组,这个数组的每个元素存放的就是虚函数的地址.指针叫做vptr,指向那个表。
而这个指针保存在相应的对象当中,也就是说只有创
建了对象以后才能找到相应虚函数的地址。
1为确保运行时的多态定义的基类与派生类的虚函数不仅函数名要相同,其返回值及参数都必须相同,否则即使加上了virtual,系统
也不进行迟后联编
2.没有继承关系,多态机制没有意义,继承必须是公有继承。
多态,虚函数,纯虚函数
多态:
是对于不同对象接收相同消息时产生不同的动作。
C++的多态性具体体现在运行
和编译两个方面:
在程序运行时的多态性通过继承和虚函数来体现;
在程序编译时多态性体现在函数和运算符的重载上;
虚函数:
在基类中冠以关键字virtual的成员函数。
它提供了一种接口界面。
允许在派
生类中对基类的虚函数重新定义。
纯虚函数的作用:
在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它
进行定义。
作为接口而存在纯虚函数不具备函数的功能,一般不能直接被调用。
从基类继承来的纯虚函数,在派生类中仍是虚函数。
如果一个类中至少有一个纯虚函
数,那么这个类被称为抽象类(abstractclass)。
抽象类中不仅包括纯虚函数,也可包括虚函数。
抽象类必须用作派生其他类的基类,而
不能用于直接创建对象实例。
但仍可使用指向抽象类的指针支持运行时多态性
重载(overload)和重写(overried,有的书也
叫做“覆盖”)的区别?
常考的题目。
从定义上来说:
重载:
是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或
许参数类型不同,或许两者都不同)。
重写:
是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:
编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就
成了不同的函数(至少对于编译器来说是这样的)。
如,有两个同名函数:
functionfunc(p:
integer):
integer;和functionfunc(p:
string):
integer;。
那么编译器做过修饰后的
函数名称可能是这样的:
int_func、str_func。
对于这两个函数的调用,在编译器间就已
经确定了,是静态的。
也就是说,它们的地址在编译期就绑定了(早绑定),因此,重
载和多态无关!
重写:
和多态真正相关。
当子类重新定义了父类的虚函数后,父类指针根据赋给它的不
同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定
的(调用的子类的虚函数的地址无法给出)。
因此,这样的函数地址是在运行期绑定的
(晚绑定)。
描述内存分配方式以及它们的区别?
1)从静态存储区域分配。
内存在程序编译的时候就已经分配好,这块内存在程序的整
个运行期间都存在。
例如全局变量,static变量。
2)在栈上创建。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数
执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集。
3)从堆上分配,亦称动态内存分配。
程序在运行的时候用malloc或new申请任意多少
的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由程序员
决定,使用非常灵活,但问题也最多。
请说出const与#define相比,有何优点?
答案:
const作用:
定义常量、修饰函数参数、修饰函数返回值三个作用。
被Const修饰的东西
都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全
检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料
不到的错误。
2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试、
全局变量和局部变量有什么区别?
是怎
么实现的?
操作系统和编译器是怎么知道的?
生命周期不同:
全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量
在局部函数内部,甚至局部循环体等内部存在,退出就不存在;
使用方式不同:
通过声明后全局变量程序的各个部分都可以用
到;局部变量只能在局部使用;分配在栈区。
操作系统和编译器通过内存分配的位置来知道的,全局变量分配
在全局数据段并且在程序开始运行的时候被加载。
局部变量则分
配在堆栈里面。
关键字static的作用是什么?
这个简单的问题很少有人能回答完全。
在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外
其它函数访问。
它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在
声明它的模块的本地范围内使用。
局部变量能否和全局变量重名?
答:
能,局部会屏蔽全局。
要用全局变量,需要使用":
:
"
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变
量。
对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义
一个同名的局部变量,而那个局部变量的作用域就在那个循环体内
如何引用一个已经定义过的全局变量?
答:
extern
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中
声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定
你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错
全局变量可不可以定义在可被多个.C文件包含的头文件中?
为什么?
答:
可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连
接不会出错
static全局变量与普通的全局变量有什么区别?
static局部变量和普通局部变量有什么区别?
stati
c函数与普通函数有什么区别?
答:
全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。
全局变量本身就是静态
存储方式,静态全局变量当然也是静态存储方式。
这两者在存储方式上并无不同。
这两者的区别虽在于
非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个
源文件中都是有效的。
而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同
一源程序的其它源文件中不能使用它。
由于静态全局变量的作用域局限于一个源文件内,只能为该源文
件内的函数公用,因此可以避免在其它源文件中引起错误。
从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。
把
全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static函数与普通函数作用域不同。
仅在本文件。
只在当前源文件中使用的函数应该说明为内部函数(s
tatic),内部函数应该在当前源文件中说明和定义。
对于可在当前源文件以外使用的函数,应该在一个头
文件中说明,要使用这些函数的源文件要包含这个头文件
static全局变量与普通的全局变量有什么区别:
static全局变量只初使化一次,防止在其他文件单元中
被引用;
static局部变量和普通局部变量有什么区别:
static局部变量只被初始化一次,下一次依据上一次结果
值;
static函数与普通函数有什么区别:
static函数在内存中只有一份,普通函数在每个被调用中维持一份
拷贝
关于内存对齐,先让我们看四个重要的基本概念:
1.数据类型自身的对齐值:
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2.结构体或者类的自身对齐值:
其成员中自身对齐值最大的那个值。
3.指定对齐值:
#pragmapack(n),n=1,2,4,8,16改变系统的对齐系数
4.数据成员、结构体和类的有效对齐值:
自身对齐值和指定对齐值中小的那个值
结构的首地址必须是结构内最宽类型的整数倍地址;另外,结构体的每一个成员起始地址必须是自身类型大小的整数倍(需要特别注意的是windows下是这样的,但在Linux的gcc编译器下最高为4字节对齐),否则在前一类型后补0
1)结构体变量的首地址能够被其最宽数据类型成员的大小整除。
编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整除的位置,这个位置作为结构体变量的首地址。
而将最宽数据类型的大小作为对齐标准。
2)结构体每个成员相对结构体首地址的偏移量(offset)都是每个成员本身大小的整数倍,如有需要会在成员之间填充字节。
编译器在为结构体成员开辟空间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为该成员大小的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要求。
3)结构体变量所占空间的大小必定是最宽数据类型大小的整数倍
static和const分别怎么用,类里面static和const可以同时修饰成员函数吗。
static的作用:
对变量:
1.局部变量:
在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。
1)内存中的位置:
静态存储区
2)初始化:
未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被
显示初始化)
3)作用域:
作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。
注:
当static用来修饰局部变量的时候,它就改变了局部变量的存储位置(从原来的栈中存放改为静态
存储区)及其生命周期(局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当
中,直到程序结束,只不过我们不能再对他进行访问),但未改变其作用域。
2.全局变量
在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。
1)内存中的位置:
静态存储区(静态存储区在整个程序运行期间都存在)
2)初始化:
未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被
显示初始化)
3)作用域:
全局静态变量在声明他的文件之外是不可见的。
准确地讲从定义之处开始到文件结尾。
注:
static修饰全局变量,并为改变其存储位置及生命周期,而是改变了其作用域,使当前文件外的源
文件无法访问该变量,好处如下:
(1)不会被其他文件所访问,修改
(2)其他文件中可以使用相同
名字的变量,不会发生冲突。
对全局函数也是有隐藏作用。
对类中的
1.成员变量
用static修饰类的数据成员实际使其成为类的全局变量,会被类的所有对象共享,包括派生类的对象。
因此,static成员必须在类外进行初始化(初始化格式:
intbase:
:
var=10;),而不能在构造函数内进行初始化,
不过也可以用const修饰static数据成员在类内初始化。
特点:
1.不要试图在头文件中定义(初始化)静态数据成员。
在大多数的情况下,这样做会引起重复定义这样
的错误。
即使加上#ifndef#define#endif或者#pragmaonce也不行。
2.静态数据成员可以成为成员函数的可选参数,而普通数据成员则不可以。
3.静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。
普通数据成员的只能声明为
所属类类型的指针或引用。
2.成员函数
1.用static修饰成员函数,使这个类只存在这一份函数,所有对象共享该函数,不含this指针。
2.静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。
base:
:
func(5,3);当
static成员函数在类外定义时不需要加static修饰符。
3.在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。
因
为静态成员函数不含this指针
不可以同时用const和static修饰成员函数。
什么是多态,多态有什么用途。
1.定义:
“一个接口,多种方法”,程序在运行时才决定调用的函数。
2.实现:
C++多态性主要是通过虚函数实现的,虚函数允许子类重写override(注意和overload的区别,
overload是重载,是允许同名函数的表现,这些函数参数列表/类型不同)。
多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。
如果函数的调用,在编译器编译期间就可以确定函数的调用地址,
并生产代码,是静态的,就是说地址是早绑定的。
而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属
于晚绑定。
3.目的:
接口重用。
封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重
用。
而多态的目的则是为了接口重用。
4.用法:
声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的
不同而实现不同的方法。
Overload(重载):
在C++程序中,可以将语义、功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型、顺
序不同),即函数重载。
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
Override(覆盖):
是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字
注:
重写基类虚函数的时候,会自动转换这个函数为virtual函数,不管有没有加virtual,因此重写的时候不加virtual
也是可以的,不过为了易读性,还是加上比较好。
Overwrite(重写):
隐藏,是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。
此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与
重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。
此时,基类的函数被隐藏
(注意别与覆盖混淆
.vector中size()和capacity()的区别。
size()指容器当前拥有的元素个数(对应的resize(size_type)会在容器尾添加或删除一些元素,来调整容器中实
际的内容,使容器达到指定的大小。
);capacity()指容器在必须分配存储空间之前可以存储的元素总数。
size表示的这个vector里容纳了多少个元素,capacity表示vector能够容纳多少元素,它们的不同是在于vector的
size是2倍增长的。
如果vector的大小不够了,比如现在的capacity是4,插入到第五个元素的时候,发现不够
了,此时会给他重新分配8个空间,把原来的数据及新的数据复制到这个新分配的空间里。
(会有迭代器失效的
问题)
三次握手
TCP三次即建立TCP连接,指建立一个TCP连接时,需要客户端服务端总共发送3个包以确认连接的建立。
在
socket编程中,这一过程中由客户端执行connect来触发,流程如下:
(1)第一次握手:
Client将标志位SYN置为1(表示要发起一个连接),随机产生一个值seq=J,并将该数据包
发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:
Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK
都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入
SYN_RCVD状态。
(3)第三次握手:
Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,
ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,
Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
四次挥手
所谓四次挥手(Four-WayWavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总
共发送4个包以确认连接的断开。
在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流
程如下图所示:
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发
送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据
了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。
首先进行关闭的一方将执行主动关
闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:
Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN
占用一个序号),Server进入CLOSE_WAIT状态。
(3)第三次挥手:
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为
收到序号+1,Server进入CLOSED状态,完成四次挥手。
线程和进程,线程可以共享进程里的哪些东西。
知道协程是什么吗
进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,即进
程空间或(虚空间)。
进程空间的大小只与处理机的位数有关,一个16位长处理机的进程空间大小为216,
而32位处理机的进程空间大小为232。
进程至少有5种基本状态,它们是:
初始态,执行态,等待状态,就
绪状态,终止状态。
线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创
建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。
因此,操作系
统中线程的概念便被引进了。
线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。
线程有时又
被称为轻权进程或轻量级进程,也是CPU调度的一个基本单位。
共享进程的地址空间,全局变量(数据和堆)。
在一个进程中,各个线程共享堆区,而进程中的线程各自维持自
己的栈
线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
(1)地址空间:
进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2)资源拥有:
进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是.
4)二者均可并发执行.
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的区别在
于:
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。
但是线程不能够
独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。
但操作系统并没有将多个线程看做多个独
立的应用,来实现进程的调度和管理以及资源分配。
这就是进程和线程的重要区别。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资
源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥
有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
协程:
定义:
协程其实可以认为是
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c+