一些补充熟背知识点


char *,const char *,char* const区别

char * 是指向变量的指针

const char * ptr(常量指针) ptr是一个指向char型常量的指针变量,不能用ptr来修改所指向的内容,

char * const ptr (指针常量) 定义一个指向字符的指针常数,即const指针,实验得知,不能修改ptr指针,但是可以修改该指针指向的内容。

了解RAII吗?介绍一下。

中文:资源获取就是初始化

RAII机制是一种对资源申请、释放这种成对的操作的封装, 通过这种方式实现在局部作用域内申请资源然后销毁资源

遵循一个步骤:

1 申请资源;
2 使用资源;
3 释放资源。

函数重载和覆盖有什么区别?

(1)函数重载。

函数重载发生在同一个类的内部。这组函数具有相同的函数名,但是参数列表不相同,在函数调用过程中根据传入的实参类型,匹配最佳的函数并调用。

(2)函数覆盖发生在子类与父类之间。父类中定义了一个虚函数,在子类中重新实现了这个函数,并且函数在子类和父类中具有相同的函数原型(函数名、参数列表),在调用函数过程中,根据对象的类型,调用相应类中的虚函数。

​ 函数重载是同一类中的不同方法,函数覆盖是不同类中的同一方法;重载函数的参数列表不同,覆盖函数的参数列表相同;重载函数调用时根据参数类型选方法,覆盖函数调用时根据对象类型选择方法。

volatile关键字作用

     volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值。

左值和右值

为了回答上面的这个问题,我们首先需要明确左值和右值的概念。C++定义了与C不相同的左值和右值的判断方法,不过说起来非常简单:凡是真正的存在内存当中,而不是寄存器当中的值就是左值,其余的都是右值。其实更通俗一点的说法就是:凡是取地址(&)操作可以成功的都是左值,其余都是右值

移动语义(move)和完美转发(forward)

所谓的移动语义是指将一块内存单元(可以是变量的内存单元也可以是临时对象的内存单元)从一个对象转移到另一个对象。

std::forward被称为完美转发,它的作用是保持原来的属性不变。啥意思呢?通俗的讲就是,如果原来的值是左值,经std::forward处理后该值还是左值;如果原来的值是右值,经std::forward处理后它还是右值。

enum和enum class的区别?

enum与class enum区别在于是否限定其作用域.
enum是不限定作用域,enum class 限定作用域
防止命名空间被污染的方法是使用enum class
enum class对枚举类型的成员的作用域进行了限定,避免同一个命名空间下的重定义产生

继承和组合的使用场景

继承是子类继承父类,父类的所有属性和方法都可以被子类访问和调用。

组合是指将已存在的类型作为一个新建类的成员变量类型,两个类之间无上下级关系。

使用场景:
当你只需要使用另外一个类的方法时使用组合。但是如果你需要使用另外一个类的作用时但你不想被其他的类访问用继承。

C++用数组好还是std::array好?

array好,数组无法直接对象赋值,无法直接拷贝等等,同时内置的数组又有很多比较难理解的地方,比如数组名是数组的起始地址等等/std::array除了有传统数组支持随机访问、效率高、存储大小固定等特点外,还支持迭代器访问、获取容量、获得原始指针等高级功能。而且它还不会退化成指针给开发人员造成困惑

std::find 可以传入 list对应的迭代器吗?

可以 list是双向迭代器

工厂模式概念和优点

​ 定义思想:工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的就是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类

优点:

  • 不需要记住具体的类名,甚至连具体参数都不需要记住
  • 实现了对象创建和使用的分离
  • 系统的可扩展性变得更好,符合开闭原则

缺点:

系统中的类是成对增加,增加了系统的复杂度和理解度

适用场景:

  • 客户端不需要知道具体产品类的名字,只需要知道其所对应的工厂即可
  • 不关心类的创建和实现的细节

操作系统怎么进行进程管理的?

进程的调度:在多个等待使用处理器的进程中,按照一定策略选择合适的进程,使之拥有处理器的使用权而进入运行。
进程的同步:对系统中的多个进程在对共享资源的使用出现竞争时进行控制和协调。
进程的控制:进程的创建和撤消以及进程状态的转换。
进程的安全:解决因多个进程争夺资源的使用权而进入 “死锁”的僵局,使系统安全顺利地运行。

操作系统怎么做到进程阻塞的?

​ 己调用了阻塞的函数,导致当前进程被放到了等待队列中,然后需要“刺激”,就会再次放回任务队列中

线程间的私有资源和共享资源

线程共享的环境包括:

​ 进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。

私有资源包括:
1.线程ID
每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。

2.寄存器组的值
由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。

3.线程的堆栈
堆栈是保证线程独立运行所必须的。

​ 线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈,使得函数调用可以正常执行,不受其他线程的影响。

4.错误返回码
由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而在该线程还没有处理这个错误,另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。所以,不同的线程应该拥有自己的错误返回码变量。

5.线程的信号屏蔽码
由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样的信号处理器。

6.线程的优先级
由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。

进程的虚拟空间布局

段名 存储内容 分配方式 生长方向 读写特点 运行态
代码段 程序指令、字符串常量、虚函数表 静态分配 由低到高 只读 用户态
数据段(静态存储区) 初始化的全局变量和静态变量 静态分配 由低到高 可读可写 用户态
BSS段 未初始化的全局变量和静态变量 静态分配 由低到高 可读可写 用户态
动态申请的数据 动态分配 由低到高 可读可写 用户态
映射段 动态链接库、共享文件、匿名映射对象 动态分配 由低到高 可读可写 用户态
局部变量、函数参数与返回值、函数返回地址、调用者环境信息 静态+动态分配 由高到低 可读可写 用户态
内核空间 操作系统、驱动程序 静态+动态分配 由低到高+由高到低 不能直接访问 内核态

操作系统如何进行上下文切换

​ 进程上下文切换,是指从一个进程切换到另一个进程运行

​ 通过中断处理,然后进行保护现场和恢复现场工这一切都依赖于堆栈指针的切换。

锁的性能开销,锁的实现原理?

在硬件层面,CPU提供了原子操作、关中断、锁内存总线的机制;OS基于这几个CPU硬件机制,就能够实现锁;再基于锁,就能够实现各种各样的同步机制(信号量、消息、Barrier等等等等)。所以要想理解OS的各种同步手段,首先需要理解本文介绍的内容,这时最原点的机制,所有的OS上层同步手段都基于此。

std::sort()

sort函数进行排序的时间复杂度为n*log2n。
原理:不是简单的快排 STL的sort()算法,数据量大时采用Quick Sort,分段递归排序,一旦分段后的数据量小于某个门槛,为避免Quick Sort的递归调用带来过大的额外负荷,就改用Insertion Sort。如果递归层次过深,还会改用Heap Sort。

函数相关判断

int *p[4]; //表示指针数组,有四个元素,每个元素都是整型指针。
*int (*p)[4]; //表示行指针,所指对象一行有四个元素。
int *p(void); //表示函数,此函数无参,返回整型指针。
int(*P)(void) ;//表示函数指针,可以指向无参,且返回值为整型指针的函数。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 351134995@qq.com

×

喜欢就点赞,疼爱就打赏