Skip to content

Latest commit

 

History

History
783 lines (481 loc) · 24.6 KB

todolist.md

File metadata and controls

783 lines (481 loc) · 24.6 KB

C++

拷贝构造函数

1.浅拷贝:默认拷贝构造函数是浅拷贝(不能用于动态分配)

class A{
private:
    int *p;
public:
    A(){p=new int();}
    ~A(){delete p;}
};
int main()
{
    A a;
    A b(a);
    return 0;
}

A a执行时p指向堆,执行浅拷贝时,只对成员赋值b.p=a.p,执行析构函数时会删除同一个堆空间,就会出错(即b.p指针悬挂)

2.深拷贝

class A{
private:
    int *p;
public:
    A(){p=new int();}
    A(const A&x){p=new int();*p=*x.p;}
    ~A(){delete p;}
};
int main()
{
    A a;
    A b(a);
    return 0;
}

完成拷贝后,a.p和b.p各指向一段内存空间,但是指向的空间内容相同!

对于一个类X, 如果一个构造函数的第一个参数是下列之一:

a) X&

b) const X&

c) volatile X&

d) const volatile X&

且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

右值引用,移动语义,完美转发,通用引用

移动构造函数(移动语义的关键)

区分左值和右值:能不能取地址!

定义移动构造函数后编译器会默认将右值进行移动构造,可以提升效率(函数传参为类对象,函数返回值为类对象)

class A{
private:
    int *p;
public:
    A(){p=new int();}
    A(A &&rhs)
    {
        p=rhs.p;
        rhs.p=NULL;
        puts("!!!");
    }
    ~A()
    {
        if(p!=NULL)delete p;
        else puts("&&&");
    }
};
int main()
{
    A a;
    A b=move(a);
    return 0;
}

右值引用,避免a的拷贝,直接将a给b,同时删除a

完美转发

forward保留参数的左右值类型

void f1(int &a)
{
    puts("&");
}
void f1(int &&a)
{
    puts("&&");
}
int main()
{
    int &&a=1;
    f1(forward<int>(a));
    return 0;
}

通用引用

1.必须满足T&&这种形式

2.类型T必须是通过推断得到的(函数模板,auto)

即可是左值引用,也可是右值引用

常量函数

注意非成员函数不能有CV[const, volatile]限定,常量成员函数不能调用

值传递,引用传递,指针传递

值传递:实参的拷贝,不改变实参的值

引用传递:实参的地址,改变实参的值

指针传递:指针指向实参的地址,改变实参的值

引用和指针传递的区别:

1.引用不能改变引用地址,只是别名不是实体,而指针是实体

2.sizeof计算变量大小时,得到引用变量大小,指针大小4

3.可以const指针,没有const引用

4.引用传递不能为空。

5.可以多级指针

sizeof

sizeof指针:指针大小

sizeof数组:数组内存大小* 数据bit大小

当数组名作为实参传入函数中,形参是指针所以不能用sizeof计算

C++可以继承string类吗?

不能,string类用final修饰,final类:禁止继承,final类成员函数:禁止重写

静态函数能定义为常函数吗

不能,常函数是操作成员变量的,而静态函数没有成员变量可说(不能访问非静态成员变量)

静态函数能定义为虚函数吗

不能,虚函数属于对象,不属于类

虚函数

子类函数覆盖基类函数(即多态)

当基类指针指向基类对象时,调用的是基类的函数。

当基类指针指向子类对象时,调用的是子类的函数。

纯虚函数virtual void f()=0

基类中没有定义,但是任何派生类中都要实现该方法

目的:使派生类只是继承函数的接口

带有纯虚函数的类称为抽象类

虚函数表(存放在全局区,每个类共用一个)建立在编译阶段,而虚函数表指针是在构造函数调用时被初始化的!

虚函数可以inline吗

inline是在编译器将函数内容替换到函数调用处,是静态编译的。而虚函数是动态调用的,在编译器并不知道需要调用的是父类还是子类的虚函数,所以不能够inline声明展开,所以编译器会忽略

虚继承

虚继承是用来解决菱形继承的问题,对于D中和B,C相同的变量,还是要通过域限定来确定。

class A{
public:
    int a;
    void f(){puts("!!");}
};
class B:virtual public A{
public:
    int a;
};
class C:virtual public A{
public:
    int a;
};
class D:public B,public C{

};
int main()
{
    D d;
    d.f();
    d.A::f();
    d.C::f();
    return 0;
}

内存对齐

为什么要内存对齐

操作系统读取内存时,按照4(或4的整数倍)个字节读取内存,保证内存对齐可节省时间

空类对象大小是1,因为需要标识该类存在,添加构造函数和析构函数还是1,把析构函数标记为虚函数后,有虚表指针32位机中为4,64位中8

程序编译的四个阶段:

1.预处理阶段(.c—.i):编译器将C程序的头文件编译进来,还有宏的替换。

2.编译阶段(.i—.s):分析检查无误后,将代码翻译成汇编

3.汇编阶段(.s—.o):汇编器将汇编程序翻译成机器语言

4.链接阶段:链接器负责处理合并目标代码,生成一个可执行目标文件,可以被加载到内存中,由系统执行

GCC常用编译选项

-c:只激活预处理,编译,和汇编,也就生成obj文件

-S:只激活预处理和编译,就是指把文档编译成为汇编代码。

-E:只激活预处理,不生成文档,需要把他重定向到一个输出文档里。

-o:定制目标名称,缺省的时候gcc 编译出来的文档是a.out

-ansi:关闭gnu c中和ansi c不兼容的特性,激活ansi c的专有特性。

-Dmacro:相当于C语言中的#define macro

-Dmacro=defn:相当于C语言中的#define macro=defn

-Umacro :相当于C语言中的#undef macro

-Idir:指定头文件路径。

-llibrary:指定库

-Ldir:定制编译的时候,搜索库的路径。

-g:指示编译器,在编译的时候,产生调试信息。

-static:此选项将禁止使用动态库,所以,编译出来的东西,一般都很大。

-share:此选项将尽量使用动态库,所以生成文档比较小,但是需要系统由动态库。

-O0 -O1 -O2 -O3:编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高

-Wall:会打开一些很有用的警告选项,建议编译时加此选项。

-std:指定C标准,如-std=c99使用c99标准,-std=gnu99,使用C99 再加上 GNU 的一些扩展。

main前调用函数

main前调用:__attribute((constructor))

main后调用:__attribute((destructor))

C++异常处理

https://www.runoob.com/cplusplus/cpp-exceptions-handling.html

访问权限

public: 能被类成员函数、子类函数、友元访问,也能被类的对象访问。

private: 只能被类成员函数及友元访问,不能被其他任何访问,本身的类对象也不行。

protected: 只能被类成员函数、子类函数及友元访问,不能被其他任何访问,本身的类对象也不行

使用private继承,父类的protected和public属性在子类中变为private

使用protected继承,父类的protected和public属性在子类中变为protected

使用public继承,父类中的protected和public属性不发生改变

面向对象和面向过程的区别

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为

STL

vector扩容:如果当前位置内存已满,新开更大的内存,把内容复制过来,释放之前的内存,然后加入新元素(两倍或者1.5倍)

为什么要成倍扩容,而不是增长固定容量

二倍扩容时:当插入n个数时,大约需要log2(n)次扩容,每次需要复制2^i个元素,一共需要复制2^1+...+2^{log2(n)}=2(n-1),平均每次pushback需要2(n-1)/n,约O(1)复杂度

固定容量为k时:当插入n个数,需n/k次扩容,每次ki个元素,一共复制k+2k+...+n/k * k=(1+n/k)* n,当k为2时,复杂度O(n^2),均摊为O(n)

为什么是两倍或者1.5倍,不是3,4倍

如果以大于2倍的方式扩容,下一次申请的内存会大于之前分配内存的总和,导致之前分配的内存不能再被使用。

vector vs list效率

插入时,vector比list快,遍历时差不多

STL空间配置器如何处理内存的?能说一下它的大概实现方案吗?为什么是8bytes的倍数?

分为两部分:大于128bytes用malloc直接申请,小于128bytes则使用一个8bytes倍数的数组来进行申请。

为8bytes的原因是为了提高效率,同时对于64位的机器而言,地址大小为8bytes

红黑树

1.节点是红色或黑色。

2.根节点是黑色。

3.每个叶子节点都是黑色的空节点(NIL节点)。

4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

memcpy源码

memcpy内未解决内存覆盖问题,下面是优化过的

void *memcpy(void *dst,const void *src,size_t len)
{
    if(dst==NULL||src==NULL)return NULL;
    void *ret=dst;
    if(dst<=src||(char*)dst>=(char*)src+len)
    {
        while(len--)
        {
            *(char*)dst=*(char*)src;
            dst=(char*)dst+1;
            src=(char*)src+1;
        }
    }
    else
    {
        src=(char*)src+len-1;
        dst=(char*)dst+len-1;
        while(len--)
        {
            *(char*)dst=*(char*)src;
            dst=(char*)dst-1;
            src=(char*)src-1;
        }
    }
}

share_ptr简单实现

template<typename T>
class Shared_ptr{
public:
    Shared_ptr():count(new int(1)),val(new T()){}
    Shared_ptr(T *p):count(new int(1)),val(p){}
    Shared_ptr(const Shared_ptr&p):count(p.count),val(p.val){
        ++*count;
    }
    ~Shared_ptr(){
        del();
    }
    T& operator *(){
        return *val;
    }
    Shared_ptr& operator =(const Shared_ptr&p){
        ++*p.count;
        del();
        count=p.count;
        val=p.val;
        return *this;
    }
    void del(){
        if(--*count==0)
        {
            delete count;
            delete val;
        }
    }
    int use_count(){return *count;}
private:
    int *count;
    T *val;
};
int main()
{
    Shared_ptr<int>a;
    printf("%d\n",a.use_count());
    Shared_ptr<int>b=a;
    printf("%d %d\n",a.use_count(),b.use_count());
    return 0;
}

计网

rtt:报文往返时间

tcp保证可靠性

1.序列号,确认应答,超时重传

2.窗口控制与高速重发控制、快速重传

(窗口大小:无需确认即可继续发送数据的最大值)

快速重传:发送端收到三次相同确认应答,会立即重发

3.拥塞控制

慢启动:开始窗口为1,每次收到确认应答(一个rtt),窗口×2

拥塞避免:设置慢启动阀值(65536)窗口达到阀值,不再指数增加,而是每次+1

ACK:确认比特,ACK=1,即确认号字段有效

SYN:同步比特,SYN=1,即连接请求

FIN:终止比特,FIN=1,即数据发送完毕请求释放连接

当接受方的接受窗口为0时还能接受数据吗?为什么?还能接受什么数据?那怎么处理这些数据呢?

可以接受。发送方会开启计时器,发送零窗口探测报文

数据:零窗口探测报文;确认报文段;携带紧急数据的报文段

可能会被抛弃

序列号seq 三次握手:

1.客户端:发送SYN连接报文,seq=x,发送完进入syn_sent状态

2.服务器:发送SYN确认连接报文,seq=y,ACK=1

3.客户端:发送ACK确认报文,ACK=y+1,两端进入established状态

四次挥手:

客户端:发送FIN终止请求,seq=x+2,ACK=y+1

服务器:发送ACK=X+3确认,发送完进入close_wait状态

客户端收到ACK后会关闭从客户端到服务器的连接,服务器会继续发送没发完的数据

服务器:发送FIN,seq=y+1,表示没有数据了

客户端:发送ACK=y+2,关闭从服务器到客户端的连接,进入time_wait状态

为什么TCP建立连接需要三次,而释放连接需要四次?

TCP释放连接时服务器的ACK和FIN是分开发送的(中间隔着数据传输),

而TCP建立连接时服务器的ACK和SYN是一起发送的(第二次握手),

所以 TCP 建立连接需要三次,而释放连接则需要四次。

TCP为什么要四次挥手? 因为TCP是全双工模式

为什么TCP释放连接后要等待2MSL

MSL:报文最大生存时间

为了确认客户端发送的最后一个ACK报文,能到达服务器,如果服务器未收到,则会超时重传FIN+ACK报文,客户端即可在2MSL时间内收到,同时保证旧的数据过期

UDP数据包最大理论长度

链路层的最大传输单元为1500,UDp包大小为1500-IP头8-UDP头20=1472 TCP 包的大小就应该是 1500 - IP头(20) - TCP头(20) = 1460 (Bytes)

TCP和UPD区别:

1.TCP面向连接,UDP无连接

2.TCP可靠,UDP尽力交付,不可靠

3.TCP点到点,UDP可多对多

4.UDP没有拥塞控制

5.TCP面向字节流,UDP面向报文

6.TCP首部开销20字节,UDP8字节

TCP粘包问题

指发送方发送的若干包数据达到接收方时首位连在了一起

1.TCP使用nagle算法(减少网络中报文段的数量),nagle收集多个小分组,在一个确认到达时一起发送。解决:使用TCP_NODELAY关闭nagle算法

2.TCP接收到数据后,应用层不会立即处理,先放在接受缓存里,再从接受缓存里读取,当接受包到缓存的速度比读取的速度大时,多个包就会被缓存,程序可能读到多个首位相连的包

多个分组不相干,但是粘在一起,需要处理,应用层处理:对数据添加起始和结束符,在数据包中添加长度

通过ip找mac地址 先ping ip然后arp -a:查询arp缓存表的IP地址和Mac地址

ping用了什么协议 ICMP:internet控制报文协议

ARP攻击 通过伪造ip和mac地址进行欺骗,实现ARP欺骗,能在网络中产生大量ARP使网络阻塞。ARP欺骗:通过欺骗局域网访问者pc的网关mac地址,使访问者pc错以为攻击者更改后的mac地址是网关的mac,导致网络不通

OSI七层模型()

物理层:

数据链路层:ARQ自动重传请求协议,CSMA停止等待协议,PPP点对点协议

网络层:IP(ipv4,ipv6)网络之间互联的协议,ARP地址解析协议

传输层:tcp传输控制协议,udp用户数据报协议

会话层:SSL安全套接字协议,TLS传输层安全协议

表示层:xdp外部数据表示协议

应用层:http超文本传输协议,IRC网络聊天协议,pop3邮局协议的第三个版本,用于接收邮件

TCP/IP 四层模型 网络接口层,网络层,传输层,应用层

区别TCP/UDP报文 看ip头中的协议标识字段,17是UDP,6是TCP

http

http协议是超文本传输协议的缩写,是用于万维网服务器传输超文本到本地浏览器的传输协议

工作原理:http无连接,每次连接只处理一个请求。http媒体独立,只要服务器和客户端知道如何处理数据,任何类型数据都可以http发送。http协议是无状态协议,如果后续需要前面的信息,必须重传。

http/https区别

1.http明文,https有TLS加密

2.http协议端口80,https443

3.https协议需要服务端申请证书,浏览器安装对应的根证书

https加密方式

对称加密:加密解密同一个密钥。非对称加密:密钥成对出现,有公钥和私钥,一个加密需要另一个解密。混合加密:首先A有公钥a,私钥b,发送给B公钥a,B随机生产密钥c,用a加密成d后发送给A,A用私钥d解密出c,AB用密钥c进行传输。https加密:A有公钥a,发送给证书颁发机构,证书颁发机构有公钥b,私钥c,机构用c加密a,生成证书签名(也被c加密),然后发送给B,B验证证书后,找到机构对应的公钥,解密出a,同时也生成证书,然后即可通信。

http返回码:

1xx:指示信息,表示请求已接受,继续处理

2xx:成功,请求已成功接受,理解,接受。200:正常处理。204:请求处理成功但没有资源可返回。

3xx:重定向,请求必须进一步操作。302:资源url已临时定位到其他位置。303资源的url已更新,明确表示客户端要使用get获取资源

4xx:客户端错误,请求有语法错误或请求无法实现。403对请求资源的访问被服务器拒绝,404无法找到请求的资源,405请求的方式不支持

5xx:服务端错误,服务器不能实现合法请求

500:服务器内部错误。501:服务器还是不具有请求功能的。502:这是服务器上一个错误网关。503:服务器维护或暂停。504:网关超时。505:http版本不支持(网关即一个网络通向其他网络的ip)

http报文结构

1.请求行:请求方法字段,url字段,http协议版本字段

2.请求头部:http客户端向服务器发送请求必须指明请求类型

3.空行:告诉服务器请求头部到此为止

4.请求数据:若方法是get,则此项为空。若方法是post,则放置的是要提交的数据

请求头

1.get:请求指定的页面信息,并返回实体主体

2.head:类似于get请求,但返回的响应中没有具体内容,用于获取报头

3.post:向指定资源提交数据进行处理请求,post可能导致新资源建立或已有资源修改

4.put:从客户端向服务器传送的数据取代指定的文档的内容

5.delete:请求服务器删除指定的页面

6.connect:http1.1协议中预留给能够将连接改为管道方式的代理服务器

7.trace:回显服务器收到的请求,主要用于测试或诊断

幂等性

调用一次和调用多次效果一样,如http的get,put,delete

get,post区别

1.url可见性:get通过url传输,可看到get的参数,pos不可见

2.传输数据大小:get传输数据大小受url大小限制,url最大长度2048,post没有长度限制

3.后退页面:get后退不会有影响,post后退会重新提交

4.缓存:get可以被缓存,post不行

5.编码方式:get只请求url编码,post支持多种编码

6.历史记录:get请求的记录会在历史记录中,post不会

7.字符类型:get只支持ascll字符,post没有限制

在浏览器输入url,按回车

1.浏览器在DNS服务器请求解析URL中域名对应IP地址

2.解析出IP后,根据IP地址和默认端口80,和服务器建立TCP链接

3.浏览器发出读取文件的http请求,该请求报文作为TCP三次握手的的第三个数据发送给服务器

4.服务器对浏览器请求做出相应,并把html文本发送给浏览器

5.释放tcp链接

6.浏览器将该html显示内容

操作系统

进程和线程的区别

1.进程是操作系统资源分配的最小单位,线程是任务执行和调度的基本单位

2.每个进程都有独立的代码和空间,切换开销较大.同一类线程共享代码和空间,切换代价小

3.操作系统中能运行多个进程,一个进程中有多个线程进行

4.系统在运行时会为每个进程分配内存空间。对于线程,除cpu,不会分配内存

5.线程是进程的一部分

**信号的生命周期?

信号产生-》信号在进程中注册-》信号在进程中的注销-》执行信号处理函数

**信号的产生方式?

(1)当用户按某些终端键时产生信号(2)硬件异常产生信号【内存非法访问】(3)软件异常产生信号【某一个条件达到时】(4)调用kill函数产生信号【接受和发送的所有者必须相同,或者发送的进程所有者必须为超级用户】(5)运行kill命令产生信号

**信号处理方式?

(1)执行默认处理方式(2)忽略处理(3)执行用户自定义的函数

创建进程的步骤

1.申请空的pcb,2.为新进程分配资源3.初始化pcb,4.将新进程插入就绪队列中(pcb进程控制块)

**线程池的作用是什么?

处理线程多并发,用一个数组保存线程,然后一直放着,如果没用就用条件变量让它休眠,如果加入一个新的任务就唤醒其中一个去执行这个任务。

进程切换发生的原因?处理进程切换的步骤?

原因:中断发生;更高优先级进程唤醒;进程消耗完了时间片;资源阻塞;

步骤:(1)保存处理器的上下文(2)用新状态和其它相关信息更新正在运行进程的PCB(3)将原来的进程移到合适的队列中【就绪,阻塞】(4)选择另外一个执行的进程,更新被选中进程的PCB,将它加载进CPU

进程间的通信方式

1.管道:半双工,只能在父子进程中

2.FIFO:不相关的进程也能够进行数据交换

3.消息队列:消息链表,存储在内核中,进程可以从中读写数据

4.信号量:在多个进程需要对共享数据进行访问的时候

5.共享内存:多个进程共享一个给定的存储区

6.UNIX域套接字:和套接字相似,但效率更高

7.网络套接字:能用于不同计算机不同进程间通信

进程状态模型

运行至阻塞

请求页面置换策略有哪些方式?他们的区别是什么?各自有什么算法解决?

全局和局部;

全局:在整个内存空间置换

局部:在本进程中进行置换

全局:(1)工作集算法(2)缺页率置换算法

局部:(1)最优算法(2)FIFO先进先出算法(3)LRU最近最久未使用(4)时钟算法

数据库

数据库索引

用途:提高查询效率

分类:顺序文件上的索引(针对指定属性值升序或降序建立索引文件),B+树索引(将索引属性组织成B+树),散列索引(散列函数映射到对应桶中),位图索引(位向量记录索引属性可能出现的值)

innoDB索引

分为聚簇索引和普通索引。聚簇索引:每张表有且仅有一个聚簇索引来存放所有数据,聚簇索引首先是主键,没有主键,选择唯一索引,没有的话,innoDB会选择隐藏的Rowid作为聚簇索引。普通索引:出来聚簇索引,其他的都是普通索引。选用的是B+树的储存结构。

B树

一个m阶的B树具有如下特征:

1.根节点至少有两个子孙

2.每一个中间节点都至少包含ceil(m/2)个孩子,最多m个孩子

3.每一个叶子节点都包含k-1个节点,m/2<=<=m

4.所有的叶子节点都位于同一层

5.每一个节点的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域划分

B树和B+树的区别

有k个子结点的结点必然有k个关键码

非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中

树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录

B+树的优点

中间节点全是索引节点,一个是可以降低树的高度,另一个是一个中间节点可以索引到更多的记录

可以直接在叶子节点层横向遍历,b树想要遍历则需要叶子节点和上层节点不停往返

为什么sql用B+树不用红黑树实现索引

B-树和B+树的特点就是每层节点数非常多,深度小,减少了磁盘io次数。但是B-树的结点都有一个data域,增加了节点大小,而B+树除了叶子节点其他节点不储存数据,磁盘io次数小,B+树另一个优点是叶子节点用指针串起,可以区间访问

聚簇索引VS非聚簇索引

聚簇索引的叶子节点就是数据节点,而非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针 CREATE NONCLUSTERED INDEX [index_name【索引名称】] ON table_name【表名称】;

封锁

排他锁(写锁)(x锁)

T对于A加上x锁,那么只允许T读取和修改A,其他都不能对A加任何类型的锁

共享锁(读锁)(s锁)

T对A加上s锁,那么只允许T读取A,其他只能对A加s锁,不能加x锁

意向排他锁(IX锁)

T要对R中某个元组加x锁,要先对关系R和数据库加IX锁

意向共享锁(IS锁)

T要对R中某个元组加s锁,要先对关系R和数据库加IS锁

锁的密度

表级锁,行级锁,页面锁

死锁产生的原因及解决办法

用户A访问表A(锁住表A),然后访问表B,用户B访问表B(锁住表B),然后访问表A,那么用户A等表B释放,用户B等表A释放,死锁产生

解决办法:调整程序的逻辑

事务管理ACID 原子性,一致性,隔离性,持久性