自己复习用,可能有错别字呜呜呜。。。。。。
说一下你理解的四种智能指针
首先说一下为什么要用智能指针:智能指针就是管理一个指针,避免程序员申请的空间在函数结束时忘记释放,造成内存泄漏这样的情况发生。然后使用智能指针很大程度上可以避免这个问题,因为智能指针就是一个类,当超出类的作用域时,类就会自动调用析构函数,析构函数就是释放资源。总的来说其作用就是在函数结束时自动释放内存空间,不需要手动释放 就是有四种智能指针
atuo_ptr
采用所有权模式,但是会存在潜在的内存崩溃
unique_ptr
实现独占式拥有或严格拥有的概念,保证同一时间内只有一个智能指针可以指向该对象,他对于避免资源泄漏特别有用,所以比auto_ptr更加的安全
shared_ptr
实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在最后一个引用被销毁的时候释放。从名字share可以看出资源可以被多个指针共享,使用计数机制来表明资源被几个指针共享,当计数等于0时,资源就会被释放。 shared_ptr是为了解决auto_ptr在对象所有权上的局限性,在使用引用计数机制上提供可以共享所有权的智能指针
weak_ptr
提供了对管理对象的一种访问手段,目的就是配合shared_ptr来协助shared_ptr工作,它只可以从一个shared_ptr或者另一个weak_ptr对象构造,它的构造和析构不会引起计数的增加或者减少。 weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果两个shared_ptr相互引用时,那么这两个指针的引用计数永远不可能下降为0,也就是资源永远不会释放,他是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间相互转化,shared_ptr可以直接赋值给它,它可以调用lock函数获得shared_ptr
C++中内存的分配情况
栈:由编译器管理分配和回收,来存放局部变量和函数参数 堆:由程序员管理,需要手动new delete malloc free 进行分配和回收,空间较大,但是会出现内存泄漏和空闲碎片的情况 全局\静态存储区:分为初始化和未初始化两个相邻区域,存储初始化和未初始化的全局变量和静态变量 常量存储区:存储常量,一般不容许修改 代码区:存放程序的二进制代码
虚函数的数据结构、如何工作的
虚函数:用virtual定义的函数为虚函数
底层机制:虚函数是使用虚函数表和虚函数表指针实现的。虚函数表是一个类虚函数的地址,用于索引类本身和类虚函数,若子类重写父类函数,则会在相应的虚函数表处替换成子类虚函数的地址。虚函数指针存在于每一个对象中,它指向对象所对应类的虚函数的地址
构造函数是不是虚函数并没有很大的影响,因为构造子类一定要构造父类。在存在继承并且析构函数需要用来析构资源时,析构函数一定是虚函数,若使用父类的指针指向子类,用delete析构函数时,只会调用父类的析构函数,不会调用子类的析构函数,造成内存泄露的问题
const 与define 的区别
编译器处理方式:const 编译时确定其值;define 预处理时进行替换 类型检查:const 有数据类型,编译时进行数据检查 ;define 无类型,也不做类型检查 内存空间:const 在静态存储区储存,仅此一份;define 在代码区,每做一次替换就会进行一次拷贝 define可以用来防止重复定义,const不行
指针和引用的区别
指针:一个变量,存储的内容是一个地址;引用:给一个已有对象起的别名 指针是一个实体,需要分配内存空间;引用不需要分配内存空间 可以有多级指针,但是不能有多级引用 自增的运算结果不一样 指针是间接访问,但是引用是直接访问 指针可以不用初始化,引用一定要初始化
函数指针和指针函数
函数指针:与整型指针类似,整型指针为指向整型的指针,函数指针就是指向函数的指针 指针函数:本质就是函数,但是返回值为一个指针
一个c++源文件从文本到可执行文件经历的过程
预处理:写好的高级语言的程序文本如hello.c,预处理根据#开头的命令,修改原始的程序,如#include<stdio.h>将把系统中的头文件插入到程序文本中
编译阶段:编译器将hello.i文件翻译成文本文件hello.s,这个是汇编语言程序。高级语言是源程序,不同的高级语言翻译的汇编语言相同
汇编阶段:汇编器将hello.s翻译成机器语言指令,把这些指令打包成可重定位目标程序
链接阶段:比如hello 程序调用printf程序,它是每个C编译器都会提供标准的标准C的函数,这个函数存在于一个名叫printf.o的单独编译好的目标文件中,这个文件将以,某种方式合并到hello.o中,连接器就是负责这种合并,得到的是可执行目标文件
C++的STL介绍
STL一共提供六大组件,包括容器、算法、迭代器、仿函数、配接器和配置器,彼此可以组合套用。容器可以通过配置器取得数据存储空间,算法可以通过迭代器存取容器内容,仿函数可以协助算法完成不同的策略变化,配接器可以应用于容器、仿函数和迭代器
容器
就是各种数据结构,如vector、list、deque、set、map,用来存放数据,从实现的角度来讲是一种类模板
算法
各种常见的算法,如sort(插入、快排、堆排序),search(二分查找),从实现的角度来讲是一种方法的模板
迭代器
从实现的角度来看,迭代器是一种operator*,operator->,operator++,operator–等指针相关操作赋予重载的了类模板,所有的STL容器都有自己的迭代器
仿函数
从实现的角度看,仿函数是一种重载了operator()的类或者类模板,可以帮助算法实现不同的策略
配接器
一种用来修饰容器或者仿函数或迭代器接口的东西
配置器
负责空间配置和管理,从实现的角度,配置器是一个实现了动态空间配置、空间管理,空间释放的类模板
STL中序列式容器的实现
vector
是动态空间,随着元素的加入,它的内部机制会自行扩充空间来存储新元素。vector维护的是一个连续的线性空间,而且普通指针就可以满足要求作为vector的迭代器
vector的数据结构中其实就是三个迭代器构成,一个指向目前使用空间头的iterator,一个指向目前使用的空间尾的iterator。当有新的元素插入时,如果目前容量够用则直接插入,如果容量不够,则容量扩充至两倍,如果两倍容量不足,就扩张至足够大的容量
扩张的过程并不是直接在原有的空间后面追加容量,而是重新申请一块连续空间,将原有数据拷贝到新的空间中,再释放原有空间,完成一次扩充。需要注意的是,每次扩充是重新开辟的空间,所以扩充后,原有的迭代器就会失效
list
与vector相比,list的好处就是每次插入或者删除一个元素,就配置或者释放一个空间,而且原有的迭代器也不会失效。STL是一个双向链表,普通指针已经不能满足list迭代器的需求。因为list的存储空间是不连续的,list的迭代器就必须具备前移和后退功能。list的数据结构中只要一个指向node节点的指针就可以
deque
vector是单向开口的连续线性空间,deque则是一种双向开口的连续性空间。所谓的双向开口就是deque支持从头尾两端进行元素的插入和删除操作。相比于vector的扩充空间的方式,deque实际上更加贴合的实现了动态空间的概念。deque没有容量的概念,因为它是动态的分段连续空间组合而成,随时可以增加一段新的空间并连接起来
由于要维护这种整体连续的假象,并提供随机存取的接口,避开了重新配置,复制,释放的轮回,代价是复杂的迭代器结构,我们应该尽可能的使用vector,而不是deque
stack
是一种先进后出的数据结构,只有一个出口,stack允许从最顶端新增元素,移除最顶端元素、获取最顶端元素。deque是双向开口的数据结构,所以使用deque作为底部结构并封闭其头端开头,就形成了stack
queue
是一种先进先出的数据结构,有两个出口,允许从最低端加入元素,取得最顶端元素,从最低端新增元素,从最顶端移除元素。deque是双向开口的数据结构,若以deque为底部结构并封闭其底端的出口,和头端的出口,就形成了queue
heap
堆并不属于STL容器组件,他是个幕后英雄,扮演priority_queue允许用户以任何次序将任何元素推入容器内,但是取出时一定是优先权最高(数值最大)的元素开始取。大根堆正具有这样的性质,适合作为priority_queue的底层机制
大根堆:是一个满足每个节点的键值都大于或等于其子节点键值的二叉树,新加入元素时,新加入的元素要放在最下一层的叶节点,即具体实现是填补在由左至右的第一个空格,然后执行所谓的上溯程序:将新的节点拿来和父节点来比较,如果其键值比父节点大,就父子对换位置,如此一直上溯,直到不需要对换或者到根节点为止。当取出一个元素时,最大值在根节点,取走根节点,要割舍最下层右边的节点,并将值重新安插至最大堆,最末节点放在根节点后,进行一个下溯程序,将空间节点和其较大的节点对调,并持续下方,直达叶节点为止
priority_queue
底层时是一个vector,使用heap形成的算法,插入,获取heap中元素的算法,维护这个vector,已达到允许用户以任何次序将任何元素插入容器内,但是取出时一定是从优先权最高的元素开始取目的
map和set有什么区别,分别又是怎么实现的
map和set都是关联容器,其底层的实现都是红黑树 由于map和set所开放的各种操作接口,RBtree也读提供了,所以几乎所有的map和set的操作行为,都是在转调RBtree的操作行为
map和set区别在于 1、map中的元素是key-value对,关键字起到索引的作用,值则表示与索引相关联的数据;set与之相对应的关键字的简单集合,set中每个元素只包含一个关键字 2、set的迭代器是const的,不允许修改元素的值;map允许修改value,但是不允许修改key。其原因是因为map和set是根据关键字排序来保证其有序性的,如果允许修改key的话那么首先需要删除该键,然后调节平衡,再插入修改后的值,调节平衡,如此一来就严重破坏了map和set的结构,导致iterator失效,不知道应该指向改变前的位置,还是指向改变后的位置。 3、map支持下标操作,set不支持下标操作。map可以用key做下标,map的下标运算符将关键码作为下标去执行查找。
osi 七层模型,每层是什么功能
物理层
处于OSI参考模型的最底层。主要的功能是利用物理传输介质为数据链路层提供物理连接,以透明的传送比特流
数据链路层
数据链路层在物理层提供比特流传输服务的基础上,在通信实体之间建立数据链路连接,传送以帧为单位的数据,通过差错控制、流量控制,变有差错的物理线路为无差错的数据链路
网络层
网络层主要的任务是通过执行路由选择算法,为报文文组通过通信子网络选择最适当的路径,他是OSI参考模型中最复杂的一层
传输层
传输层是向用户提供可靠的段到段服务,透明的传送报文
会话层
会话层的主要目的是组织同步的两个会话用户之间的对话,并管理数据的交换
表示层
表示层主要用于处理两个通信系统间信息交换的表示方式,它包括数据格式变换、数据加密与解密、数据压缩与恢复等功能
应用层
应用层是OSI参考模型的最高层。应用层不仅要提供应用进程所需要信息交换和远程操作,而且还要作为应用进程的用户代理,完成一些为进行语义上的有意义的信息交换所必须的功能
网络分层(四、五层)
四层:链路层、网络层、传输层、应用层 五层:物理层、链路层、网络层、传输层、应用层
应用层
直接为用户的进程提供服务
传输层
负责两个主机中进程之间的通信提供服务,由于一个主机可以同时运行多个进程,因此传输层具有复用和分用的功能。复用就是多个应用层可以同时使用下面的运输层的服务,分用则是运输层把收到的信息分别交付给上面应用层中相应的协议
网络层
负责向两个主机提供通信服务。在发送数据时,网络层把传输层产生的报文段或者用户数据报封装成分组或者包进行传输。网络层的另一个任务就是选择合适路由,使源主机传输层所传下来的分组,能够通过网络中的路由找到,目的主机
链路层
在两个相邻节点之间传输数据时,数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻节点间的链路上透明的传输帧中的数据,每一帧包括数据和必要的控制信息
物理层
主要的功能是利用物理传输介质为数据链路层提供物理连接,以透明的传送比特流
tcp的握手和挥手细节
三次握手的过程
第一次:客户端发含SYN位,SEQ_NUM=S的包到服务器(客->SYN_SEND) 第二次:服务器发含ACK,SYN位且ACK_NUM=S+1,SEQ_NUM=P的包到客户机。(服务器发送SYN->RECV) 第三次:客户机发送ACK位,ACK_NUM=P+1的包到服务器
第一次握手:主机A通过向主机B 发送一个含有同步序列号的标志位的数据段给主机B,向主机B 请求建立连接,
通过这个数据段, 主机A告诉主机B 两件事:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我。
第二次握手:主机B 收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机
A,也告诉主机A两件事:我已经收到你的请求了,你可以传输数据了;你要用那个序列号作为起始数据段来回应我
第三次握手:主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,
我现在要开始传输实际数据了,这样3次握手就完成了,主机A和主机B 就可以传输数据了。
四次挥手过程
第一次:客户机发含FIN位,SEQ=Q的包到服务器(客户机发送FIN_WAIT_1) 第二次:服务器发送含ACK且ACK_NUM=Q+1的包到服务器 (服务器是CLOSE_WAIT,客是FIN_WAIT_2) 第三次:服务器发送含FIN且SEQ_NUM=R的包到客户机 (服务器是LAST_ACK,客户机是TIME_WAIT) 第四次:客户机发送最后一个含有ACK位且ACK_NUM=R+1的包到客户机 (服务器CLOSED)
第一次: 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求 ;
第二次: 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1;
第三次: 由B 端再提出反方向的关闭请求,将FIN置1 ;
第四次: 主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.。
为什么握手是三次,挥手是四次
对于握手:握手只需要确定双方通信时的初始化序号,保证通信不会乱序。其中第三次握手的必要性:假设服务端的确认丢失,连接并未断开,客户机超时重新发送连接请求,这样服务器就会对同一个客户保持多个连接,造成资源浪费。 对于挥手:TCP是双工的,所以发送方和接收方都是需要FIN和ACK,只不过有一方是被动的,所以看上去是四次挥手
tcp和udp的区别
tcp是传输控制协议,是面向连接的协议,也就是说。在收发数据前,必须和对方建立可靠的连接
udp是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单的去抓来自应用程序的数据,并尽可能快的把它扔到网络上。在发送端,UDP传送数据的速度仅仅受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。由于传输数据不需要建立连接,因此就不需要维护连接状态,包括收发状态等,因此一台服务器可以同时向多个客户机传输相同的消息
- 基于连接与无连接
- 对于系统资源的要求(TCP较多、UDP少)
- UDP程序结构较简单
- 流模式与数据报模式
- TCP保证数据正确性,UDP可能丢包
- TCP保证数据顺序,UDP不保证
队列和栈的区别
- 队列是先进先出,栈是先进后出
- 栈是只能在表的一端进行插入和删除操作的线性表;队列是只能在表的一段进行插入和在另一端进行删除的线性表
- 栈只能从头部取数据,也就是最先放入的数据需要遍历整个栈最后才能取出来,而且在遍历数据时还得为数据开辟临时空间,保持数据在遍历前的一致性;队列则不同,他是基于指针进行遍历,而且可以从头或者尾部开始遍历,但是不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影响数据结构,速度要快
static关键字的作用
ip的两种类型 ipv4和ipv6
IP地址相当于电脑的电话号码,IPV4的长度是32位;IPV6的长度是128位
排序算法知道哪些,各自时间复杂度,空间复杂度
排序算法 | 平均时间复杂度 | 最好的情况 | 最坏的情况 | 空间复杂度 |
---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 希尔排序 | O(nlogn) | O(nlogn^2) | O(nlogn^2) | O(1) | 归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn) | 计数排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | 桶排序 | O(n+k) | O(n+k) | O(n^2) | O(n+k) | 基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) |
冒泡排序
- 比较两个相邻的元素。如果第一个比第二个大,就交换他们两个
- 对每一对相邻的元素做同样的工作,从开始第一对到结尾的最后一对,这样在最后元素应该是最大的
- 针对所有的元素重复以上的步骤,除了最后一个元素
- 重复步骤1-3,直到排序的完成
插入排序
分为已排序和未排序。初始已排序区间就只有一个元素,就是数组第一个元素,然后遍历未排序的每个元素在已经排序区间里找到合适的位置插入并保证数据一致有序
选择排序
分为已排序和未排序的区间。每次会从未排序的区间中找到最小的元素,将其放到已排序区间的末尾
快速排序
先找到一个枢纽,在原来的元素中根据这个枢纽划分,比这个枢纽小的元素排在前面,比这个枢纽大的元素排在后面;两个部分依次进行递归排序下去直到最终有序
linux中删除文件用什么命令
rm -rf删除文件夹 rm -rf删除文件
什么情况会有可能导致栈溢出
- 函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归就无法返回。再有就是当函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出
- 动态申请空间使用之后没有释放。
- 数组访问越界
- 指针的非法访问
红黑树是什么,介绍一下定义、特点、用途
红黑树是一种自平衡二叉查找树,一种特殊的二叉查找树,是一种数据结构。红黑树的每个节点上都有存储位表示节点的颜色,可以使红色也可以是黑色。 红黑树的特性有:每个节点是黑色或者红色; 根节点是黑色; 每个叶子节点是黑色; 如果一个节点是红色的,那他的子节点就是黑色的; 每个节点到叶子节点所经过的黑色节点的个数是一样的。
典型的用途是关联数组,存储有序数据 C++中的STL,map和set都是红黑树是实现的 JAVA中的TreeMap实现 Linux非实时任务调度 Linux虚拟内存中的应用
进程与线程的区别、死锁、死锁有什么业务场景
进程和线程的区别
线程和进程都是操作系统所体会的程序运行的最基本的单元,系统利用该基本单元实现系统对应用的并发性。他们的区别在于: 1、一个程序至少有一个进程,一个进程至少有一个线程 2、线程的划分尺度小于进程,使得多线程程序的并发性高。 3、进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率 4、线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位。线程基本上就不拥有系统的资源,只拥有一点在运行中必不可少的资源,但是它可与同属一个进程的其他线程共享进程所拥有的全部资源 5、一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行
死锁
操作系统中的死锁定义为系统中两个或者多个进程无期限的等待永远不会发生的条件,系统处于停滞状态,这就是死锁
产生死锁的原因主要是: 系统的资源不足 进程运行推进的顺序不合适 资源的分配不当
产生死锁的四个必要的条件:
- 互斥条件:一个资源每次只能被一个进程进行使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得资源,在未使用之前不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相连的循环等待资源关系
哈希函数用在哪些场景
将任意长度的二进制值串映射为固定长度的二进制值串,该映射规则就是哈希算法
哈希算法满足的条件
- 从哈希值不能反向推出原始数据(单向哈希)
- 对输入的数据非常的敏感,哪怕原始的数据只是修改了一个bit,最后的到的哈希值也大不相同
- 散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小
- 哈希算法的执行效率要尽量高
哈希算法的应用场景
1.安全加密:最常用的加密的哈希算法是MD5和SHA。对于加密来说两个方面很重要:很难根据哈希值反向推出原始数据;尽量的减少碰撞 2. 唯一标识:获取哈希值作为惟一的标识 3. 数据校验:对得到的文件进行哈希,然后得到的哈希值和文件中的校验位进行对比,就可以得知文件有没有被篡改
数据结构了解哪些,用过哪些,数组和链表的区别
数据结构
数据结构分为八类:数组、栈、队列、链表、树、散列表、堆、图
数组和链表的区别
数组:是一组具有相同数据类型的变量的集合,这些变量称为之为元素的集合。每个元素都有一个编号,称之为下标,可以通过这个下标来区分并访问数组元素,数组元素的个数叫做数据的长度
链表:链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的特性就是在任意位置插入和删除元素都是非常快,不需要一定其他元素。对于单向链表而言,链表中的每一个元素都要保存一个指向下一个元素的指针。对于双向链表而言,链表中的每个元素既要保存指向下一个元素的指针,又要保存指向上一个元素的指针。对于双向循环链表而言,链表中的最后一个元素保存一个指向第一个元素的指针
区别
- 逻辑结构
数组:在内存中连续;使用之前需要固定长度,不支持动态改变数组的大小;数组元素增加时可能会数组越界;数组元素减少时,会造成内存浪费;数组增删时需要移动其他元素
链表:采用动态内存分配的方式,在内存是不连续的;支持动态增加或者删除元素;需要时可以使用malloc或者new来申请内存,不需要时使用free或者delete来释放内存
-
内存结构 数组:从栈上分配内存,使用方便,但是自由度小 链表:从堆上分配内存,自由度大,但是要注意内存泄漏 -
访问效率 数组:数组在内存中顺序存储,可以通过下标进行访问,访问效率高 链表:链表访问效率低,如果你想要访问某个元素就需要从头遍历 -
越界问题 数组:大小是固定的,所以存在访问越界的风险 链表:只要可以申请得到链表空间,链表就无越界风险
多线程
多线程是指从软件或者硬件上实现多个线程并发执行的技术
ip和mac地址
MAC地址是数据链路层和物理层使用的地址,MAC地址的分配是基于制造商的,网卡在通讯的时候通过mac地址相互识别,MAC地址的长度是48位
IP地址是网络层及以上层使用的地址,IP地址的分配是基于网络拓扑的,IP地址是可以更改的,IP地址的长度是32位
怎么起个线程,多个线程之间的锁怎么弄,怎么加锁
C++ builder中虽然有Tthread对象说明了线程的概念,但是Tthread对象本身并不完整,需要在TTthread下新建其子类 线程之间的锁有:互斥锁、条件锁、自旋锁、读写锁、递归锁。一般而言,所得功能越强大,性能就会越低
互斥锁
互斥锁用于控制多个线程对它们之间共享资源互斥访问的一个信号量。也就是说为了避免多个线程在某一时刻操作同一个共享资源
某一个时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待
条件锁
条件锁就是所谓的条件变量,某一个线程因为某个条件未满足时可以使用条件变量使程序处于阻塞状态。一旦条件满足以信号量的方式唤醒一个因为条件处于而被阻塞的线程
自旋锁
如果一个线程想要获得一个被使用的自旋锁,那么它会一直占着CPU请求这个自旋锁使得CPU不能去做其他的作用,直到获取这个锁为止,但是互斥锁就会让CPU去处理其他的事情
读写锁
计算机中某些数据被多个进程共享,对数据库操作有两种:一种是读操作,就是从数据库中读取数据不会去修改内容;另一种就是写操作,写操作就会修改数据库中存放的数据,因此我们允许在数据库上同时执行多个读操作,但是一个时刻只有一个写操作来更新数据
操作字符串的一些方法
string的大小和容量
- size()和length():返回string对象的字符个数,它们执行的效果是一样
- max_size():返回对象最多包含的字符数,超出会抛出length_more异常
- capacity():重新分配内存之前,string对象能包含的最大字符数
string的字符串比较
- 比较操作符:>,>=,<,<=,==,!= 这些操作符根据当前字符特性,将字符按字典顺序进行逐一比较,字典排序靠前的字符小
- 成员函数compare(),支持多参数输入,支持用索引值和长度定位子串进行比较,前面减去后面的ASCII码,>0返回1,<0返回-1,相同返回0
string的插入:push_back()和insert()
- push_back()是尾插一个字符串
- insert()在制定的位置前插入字符
string拼接字符串:append()、+ string的遍历:借助迭代器或者下标法
- 正向迭代器:str.begin()、str.end()
- 反向迭代器:str.rbegin,str.rend()
string的删除:erase() string的字符替换:replace() string大小写的转换:tolower()和toupper() string的查找:find() string的排序:sort() string的分割/截取字符串:substr()
core dumped文件
core意思是内存,dump就是扔出来。 在开发和使用unix程序时,有时候就莫名其妙的down了,却没有任何的提示,这个时候就可以查看有没有形成core.进程号的文件生成,这个文件就是操作系统把程序down掉时的内存内容扔出生成的,可以作为调试的参考 core dump又叫核心转储,当程序运行过程中发生异常,程序异常退出时,由操作系统把程序当前的的内存内容状况存储在一个core文件中,叫core dump
core文件生成的位置一般于运行程序的路径相同,文件名一般是core.进程号
使用core文件:gdb -c core 文件路径 进去之后输入where回车,就可以显示程序是在哪一行down了,在哪个函数中了
构造函数的调用顺序
基类构造函数、对象成员构造函数、派生类本身的构造函数
析构函数的调用顺序
派生类本身的析构函数、对象成员的析构函数、基类构造函数
c++11的新特性
- 提高运行效率的语言特性:右值引用、泛化常量表达式
- 原有语法的实用性增强:初始化列表、统一的初始化语法、范围for循环、final和override
- 语言能力的提升:空指针nullptr、default和delete、长整数
C++中重载、重写和重定义的区别
- 重载:overload,是指在同一可访问区内被声明的几个具有不同参数列表的同名函数,依赖于C++函数名字的修饰会将参数加在后面,可以是参数类型,个数,顺序的不同
- 重写:override,派生类中重新定义父类中除了函数体外完全相同的虚函数,注意被重写的函数一定不能是static的,一定是虚函数的,且其他一定要完全相同
- 重定义:派生类重新父类中相同名字的非virtual函数,参数列表和返回类型都可以不同
软件测试级别
- 单元测试:单元测试是对软件组成单元进行测试。其目的就是检验软件基本组成单位的正确性。
- 集成测试:联合测试、组装测试,将程序模块采用适当的集成策略组装起来,对系统接口及集成后的功能进行正确性检测的测试工作主要是检查软件单位之间的接口是否正确。
- 系统测试:将软件系统看成一个系统的测试。包括对功能、性能以及软件所运行的软硬件环境进行测试。系统测试的目的是对软件系统进行全面的测试,确保最终软件系统满足产品需求并且遵循系统设计
- 验收测试:验收测试是部署软件之间的最后一个测试操作。他是技术测试的最后一个阶段,也称为交付测试。验收测试的目的是确保软件准备就绪,并且可以让最终用户将其用于执行软件的既定功能和任务
软件测试类型
- 功能测试:也叫黑盒测试,功能测试指测试软件各个功能模块是否正确,逻辑是否正确
- 性能测试:是指验证软件的性能可以满足系统规格给定的指定要求的性能指标
- 配置测试:用硬件来测试软件的运行情况(在不同的主机上、不同的组件上、不同的外设接口内存的运行情况)
- 强度测试:强度测试也是性能测试,他是在系统资源特别低的情况下软件系统运行情况。可以找出因资源不足或者资源争夺而导致的错误
- 负载测试:通过在被测系统上不断加压,知道达到性能指标的极限。数据在超负荷的环境中运行,程序能否承担,确定确保系统在超出最大预期工作量条件下仍能正常运行
测试方法
动态测试、静态测试、黑盒测试、白盒测试、灰盒测试
黑盒测试
- 等价类划分:将系统的输入域划分成若干个部分,然后从每个部分选取少量的代表性数据进行测试
- 边界值分析法:对等价类划分的一种补充,因为大部分错误都在输入输出的边界上,
- 错误猜想法:错误猜想法主要是针对系统对于错误操作时对于操作的处理法的猜测法,从而设计测试用例
白盒测试
- 语句覆盖:就是设计若干个测试用例,运行被测程序,使得每一个可执行语句至少执行一次
- 判定覆盖:使得设计的测试用例保证程序中每个判断的取值分支至少经历一次
- 条件覆盖:选择足够的测试用例,使得运行这些测试用例时,判定每个条件的所有结果至少出现一次
- 判定条件覆盖:设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次
- 条件组合覆盖:选择足够的测试用例,使所有判定中各条件判断结果的所有组合至少出现一次,满足这种覆盖标准成为条件组合覆盖。
- 路径覆盖:是每条可能执行到的路径至少执行一次
灰盒测试
定义:介于黑、白盒测试之间的,关注输出对于输入的正确性,同时也关注内部表现
静态测试
无需执行被测程序,而是通过评审软件文档或代码,度量程序静态复杂度,检查软件是否符合编程标准,借以发现编写的程序的不足之处,减少错误出现的概率
动态测试
1,动态测试是指通过运行被测程序,检查运行结果和预期结果的差异,并分析运行效率,正确性和健壮性等
2,静态(看外观)和动态(发动车走一段路)可以用买车来说明
|