总结1

总结

虽然是没找到实习的一天,但还是总结一下这一周为面试的准备的细节

后端

语言(cpp):

  1. 多态
    静态多态: 函数的参数,返回值的变化(函数签名,c++_filt)(overload),模版可能也算
    动态多态: 用父类的指针去操纵子类(派生类), 依然能够表现子类的特性(函数)

机制: 虚函数的重写(override)
每一个含有虚函数的类都有虚函数表的指针,指向这个类的虚函数表
虚函数表中维护者虚函数指针,子类重写了父类的虚函数,对应的表项就会更新成子类的指针

虚函数表还有type_id的信息,可以用typeid().name()获取动态的类型

对于多继承的情况,可能存在多个虚函数表的指针,它们指向同一个虚函数表的不同offset
所以存在主继承

菱形继承:子类存在多个继承链到同一个父类,可以采用虚继承,保证只有一个父类的成员变量和成员函数

析构函数要求是虚函数,因为子类可能需要delete一些自己申请的资源;如果不是析构函数,那么调用直接调用父类的析构函数,资源就没有释放
构造函数不应该是虚函数,因为实力化需要先分配内存,然后调用构造函数和初始化虚函数表的指针;此时无法根据虚函数表实现多态。而且正确应该先调用父类构造函数用于父类成员的初始化,然后再调用子类的构造函数

分配内存, 父类构造函数,子类构造函数, 子类析构函数,父类析构函数,回收内存

纯虚函数类似java的接口,需要子类实现父亲的方法

override 关键字,会要求该方法是重写父类的方法,相当于提供了编译器检查,也能给代码的使用者和维护者良好的可读性

这一方面const也是如此

  1. 组合和继承(代码复用的方式)

  2. 指针和引用
    指针是对象的地址,引用可以看作是对象的别名,也可以看作是const指针
    传参时:引用往往需要切实的对象,指针则可以空指针。而且指针可以指向同一类型或者是派生类型的对象,引用则和对象绑定了。

智能指针:
uniqie_ptr 离开作用域是自动完成析构, std::move() 转移对象的所有权,可以基于stack的特性(作用域)实现
shared_ptr 检查引用计数,当引用计数为0时,完成指针的销毁
weak_ptr: 避免shared_ptr的循环依赖导致无法销毁对象,weak_ptr不会增加引用计数,也不会有对象的所有权

  1. static

  2. 文件外部,意味这只有该文件的编译单元能够看见;其他文件的编译单元是不会使用到这个变量

  3. 局部变量,意味这存储再静态存储区,而且只初始化一次;程序:(代码段,数据段(全局变量,为初始化的全局遍历那个),常量("dn"),堆,栈), 延长的生命周期,但作用域没有变化

  4. class内部:静态成员变量,表示所有类的实力共享的,静态成员函数:只能访问静态成员变量;普通的成员函数会传递this指针,静态成员函数不会传递this指针

类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

在一个类的静态成员中去访问其非静态成员之所以会出错是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。

  1. 字节对其
    方便存取对应的成员
    64bits 8bytes

  2. malloc free new delete new[] delete[]
    malloc new表达式:类型, 不用知道size; 空指针, 异常处理; 调用构造函数
    new[] delete[] 内存起始位置回收内存

  3. 禁止默认拷贝构造函数
    当成员中存在指针的时候,默认的拷贝构造函数会无法完成深拷贝,造成错误
    所以如果自己不打算实现拷贝构造函数,禁止(=delete)应该是一个好的做法

  4. 栈/堆分配对象
    栈: private operator new (因为new operator 总是先调用operator new;所以只要禁用就能使得类对象只能建立在栈上)
    堆:
    当对象建立在栈上面时,是由编译器分配内存空间的,调用构造函数来构造栈对象。当对象使用完后,编译器会调用析构函数来释放栈对象所占的空间。编译器管理了对象的整个生命周期。如果编译器无法调用类的析构函数,情况会是怎样的呢?比如,类的析构函数是私有的,编译器无法调用析构函数来释放内存。所以,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。
    但同时需要自己提供destroy函数进行空间的释放

  5. 把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果据成员是指针,则const成员函数并不能保证不修改指针指向的对象,编译器不会把这种修改检测为错误

智能指针 RAII 互斥锁的 lock_guard lock_unique

  1. 三次握手
    主动发起连接方 sync isn_init = x
    接受方: ack, aync, seq = y, ack=x+1
    发起方: ack seq=x+1 ack=y+1
    三次:假ip(ip flood); 请求连接的网络延迟

四次挥手:
主动断开:
接受方可能还有数据需要传输完成

TCP UDP

UDP为什么不可靠:?

拥阻塞控制,流量控制

NAT, DNS, ICMP, https(SSL)

浏览器点击连接:(越往下层加越多的表头)

操作系统:

线程,进程:
进程间通信: 共享内存, 管道,socket

线程间切换: PCB/TCB task_struct 存储在内核态;内核态,用户态;
用户态怎么进入内核态: 异常(int 80指令,软中断,查询中断向量表,中断处理程序)-> 系统调用;中断,键盘输入(控制器)

进程间的切换可能会导致一页表的切换,可能会有TLB miss和TLB填充
线程间的切换更多的是相关的寄存器的保存,以及栈底,栈真寄存器指向新的栈

进程间的虚拟地址是独立的(两个进程的虚拟地址映射的是不同的物理内存)

在内核模式中,可以随意的访问各种硬件资源。能够执行任何指令*(特权指令)和访问任意的内存。内核系统往往保留最低等级最受信任的功能(OS)。内核太的崩溃是灾难性的,会导致整个PC的崩溃
用户模式需要委托给系统API才能访问硬件或者内存。由于这种 i隔离提供了保护,因此用户态模式下的崩溃是可以恢复的。

原文地址:https://www.cnblogs.com/fridayfang/p/14668928.html