c++内存区域:
堆
栈
静态/全局区:全局变量和静态变量同为静态存储区,区别: 全局作用域整个文件
static关键字:
1.修饰 变量 函数
2. 在函数体内声明 作用范围:此函数体内,其他函数内是不能访问该变量的。
在模块内声明 作用范围:源文件内声明:只能在该文件内访问
头文件中声明:只要包含该头文件就可以访问该函数/变量
作用:避免命名重复。
3类中声明: 成员变量:
成员函数: 静态成员函数只能访问该类的静态成员变量。不接受this(本类对象)
作用范围:属于类拥有
const关键字:
** 修饰变量: 注意: c 语言中 局部(栈区) :可以用指针修改 全局(全局静态区,只读):不能用指针修改
c++ : 加载到符号表,允许指针可以修改,但是不会成功。 用的都是符号表里的。而不是该内存
** 修饰指针变量: char *const ptr; 常量指针(限定指针的指向)。
const char *ptr; 指针常量 (指针指向的是一个常量,该常量不可修改)。
作用: 1避免修改 2.和define 不同 可避免多次内存分配 3.类型检查,作用域检查
修饰函数参数:
修饰函数返回值: c++运算符重载 a+b =c;避免出现 将c赋值给a+b
类中常成员函数: 注意: 变量不可修改 常成员函数只能访问常变量
作用:1避免修改 2.用于函数重载
指针和引用
区别: 初始化: 引用定义必须初始化,且不能修改。
指针,有自己的实际地址, 而引用没有自己的实际地址。
指针是对象 引用不是一个对象。
做函数参数: 指针:可以实现多态
引用:
野指针: 指的是没有被初始化的指针。 可能指向随意地址该地址可能不存在出现段错误。
使用指针是 要做检测。
c++标准库
malloc ,free ,new ,delete 的区别
定义:malloc free是c的库函数
** new delete是c++当中的操作符–操作符可以重载。
使用方式:new自动计算所需的内存分配,malloc需要手动计算所需分配内存
new返回的是对象类型的指针,malloc返回的是void*,需要转换所需的类型。
delete释放内存是需要对象类型指针,free是void*类型的指针。
new内存失败会抛出异常,free失败返回null。
** new实在free store上分配内存;malloc从堆上分配内存。 --free store 上既可以是再堆上也可以是在其他地方,比如内存池。
new先调用operator new,申请足够内存,然后调用构造函数 初始化成员变量,返对象类型指针回;
delete先调用析构函数,再调用operator delete函数释放内存。
delete,free调用后,内存不会立刻被释放(只是告诉操作系统,这块内存不再使用),指针也不会置空。
extern关键字的作用:
声明一个全局变量或函数 extern 在声明是不能修改,
原理:引用还没有声明的变量或者函数,这个变量或函数在其他地方声明了。
使用场景: 1. 声明外部变量或函数
2. c++调c c++调用c编译的变量或者函数(跨文件访问变量,c++调用c,c++的命名时支持重载的,命名时不一样的)
extern "C" int var_c(); //c++类外声明该为c编译的
extern "C" void func_c(); //
int main(){
extern int var; //声明
extern void fun(); //声明
fun();
extern void func_other(); //声明 c调用c
func_other();
return 0;
}
int var = 100;
void fun(){
printf("fun var=%d\n",var);
}
#include<stdio.h>
int var_other =99;
void func_other(){
printf("func_other");
}
gcc编译多个文件: extern_c.c extern_cpp.c
gcc extern_c.c -c //编译成 .o
gcc extern_cpp.c -c //编译成 .o
多个文件链接: gcc extern_c.o extern_cpp.o -o extern_cp_c //将两个.o 链接成可执行程序
单个文件链接: gcc extern.c -o exter_c
优化编译: 优化的是头文件
简述strcpy,sprintf与memcpy的区别
同: 实现字符串拷贝功能
异:
功能上:
strcpy实现(字符串)拷贝,遇到\0结束; string中的
sprintf(多类型)格式化字符串; c中的
memcpy(可操作的内存地址)实现内存块拷贝,根据size的大小来复制,任何类型。string中的
执行效率上:memcpy最快,strcpy次(需要检测/0),sprintf最慢(需要类型转换)
操作对象上:
bcopy 与memcpy
void *bcopy(dest,src,length)
将src拷贝到dest,拷贝字节长度length,
C/C++中的类型转换以及使用场景
c的转换:(T)exp T(exp)没有检测机制。当线上出错时很难找到bug
c++类型转换(4种):
static_cast(exp) :
类层次间转换:-上行转换(安全);下行(基类转派生类不安全)没有动态类型检测 ;
基本类型转换:
空指针转换为目标类型空指针:
non-const 抓换为const:
局限:不能去掉const,volitale等属性。
const_cast(exp):
去掉对象指针或对象引用const属性,目的:修改指针(引用)的权限,可以通过指针或引用修改某块内存的值
dynamic_cast(exp):
用于多态,在运行时进行类型转换。
在一个类层次结构中进行安全的类型转换。把基类指针或引用转换为派生类的指针(引用)。
因为引用不存在空引用,转换失败会抛出bad_cast异常
relnterpret_cast(exp)
改变指针(引用)类型
将指针(引用)转换为一个整形。
将整形转换为指针(引用)
T必须为指针,引用,整形,函数指针,成员指针
仅仅是bit位的拷贝,没有安全检查。
作用: 去掉const属性用const_cast。
基本类型转换static_cast。
多态之间的类型转换dynamic__cast。
不同类型的指针类型转换relnterpret_cast
什么时候生成默认构造函数
空的类定义,不会生成默认构造函数,没有意义:即便里面包含成员变量,还是不会生成默认构造函数,编译器不知道用什么值区初始 化。(不需要生成的尽量的不会生成默认构造函数,实在不行才会生成默认构造函数)
什么时候生成默认构造函数:
类A内有成员对象B,类B有默认构造函数:为了调用B的默认构造函数,不得以为A类生成默认构造函数。
**类的基类提供了默认构造函数:**子类构造函数要初始化父类,在初始化成员变量;若父类没提供默认构造函数,子类也无需提供默认构 造函数;若父类提供了默认构造函数,子类不得不生成默认构造函数。
**类内定义了虚函数:**为了实现多态机制,需要为类维护一个虚函数表。类所有的对象都需要保存一个指向虚函数指针。类需要初始化该虚函数表指针所以不得不提供默认构造函数来初始化该虚函数指针。
**类使用了虚继承:**虚基类表记录了类继承的所有的虚基类对象在本类定义的对象内偏移位置,为了实现虚继承,对象子初始化的时候需要维护一个指向虚基类表指针,不得不提供默认构造函数来初始化虚基类表指针。
什么时候生成默认拷贝构造函数
有一个不得不的条件: 如果不提供默认拷贝构造函数,编译器在编译时会做什么?会按照为拷贝的方式进行拷贝(一字节一字节的拷贝),有些时候位拷贝会出现不是我们所预期的行为。
什么时候必须生成默认拷贝构造函数呢?
1类成员也是一个类,该成员类有默认拷贝构造函数。为了让成员类的默认拷贝构造函数能够被调用到,不得不为类生成默认拷贝构造函数
2类继承自一个基类,这个基类有默认拷贝构造函数,子类执行拷贝构造函数,先调用父类的拷贝构造函数,为了调用到父类的拷贝构造,不得不生成默认的拷贝构造函数。
3.类成员种包含一个或多个虚函数,为了实现多态需要为类维护一个虚函数表,类所有对象都需要保存一个指向该虚函数的指针。如果不提供不提供默认拷贝构造函数,会进行位拷贝,那么虚函数表指针就会丢失,所以不得不为类生成默认拷贝构造构造函数,完成虚函数表指针的拷贝。
4类继承自一个基类,这个基类有一个或者多个虚函数,如果不提供默认构造函数会进行位拷贝,那么基类拷贝构造函数不饿能调用,从而虚函数表指针会丢失,不得不为类生成默认拷贝构造构造函数,依此完成基类拷贝构造函数的调用,从而完成虚函数表指针的拷贝
c++什么是深拷贝浅拷贝
拷贝的情况: 1类的对象构建一个新的类对象 2.函数传参为类对象 3函数返回值是类对象
浅拷贝;对象种的成员数据的简单赋值
深拷贝:对对象中的动态成员(指针)重新动态分配空间,或者重新分配资源。
vector底层实现
底层实现了一个动态数组。
**类构成:**class vector:protected_Vector_base probected继承:基类的public在子类中将变成protected:其他权限不改变
_Vector_base: _M_start 容器起始位置, _M_finish容器结束位置 _M_end_of_storage 动态内存最后一个元素的下一个位置。
构造函数: 无参构造:没有申请动态内存 性能优先
**初始化元素个数构造:**申请了动态内存,避免多次动态申请内存,从而影响性能
插入元素: 检查空间,是否需要动态分配内存,是否需要翻倍,插入最后位置;插入非最后位置,检查空间,是否需要动态分配
内存,是否需要翻倍。待插入位置之后元素往后平移一位,然后插入元素。
**删除元素:**删除最后:_M_finish 往前移动一位,删除元素不会释放已经申请的内存;
删除非最后一个元素,删除待删除位置之后元素向前平移一位,删除元素不会释放已经申请的内存。
**读取元素:**操作符[] ,at比操作符下标多了一个检查越界的动作,他们都是返回具体元素的引用。
修改元素: vector不支持直接修改某个位置的元素,通过读取元素,获取引用,然后修改引用的值。
释放空间:1.swap一个空容器 2.先.clear() 清空元素,但是不会释放内存 c11 shrink_to_fit释放掉未使用的内存
vector内存增长机制
特点:内存空间只会怎加不会减少,且内存是连续的。 不同平台的增长方式不同,linux 是倍张 win是0.5张
增长特征:无参构造,连续插入一个,增长方式1248… 有参构造,连续插入一个,增长方式10,20,40
vector的reserver和resize的区别
同:容器内元素不受影响,起到增加的作用,对于缩小直接无视
异:reserve只能增加vector的capacity,但是它的size是不会改变的;注意:不能减少
resize既能增加vector的capacity,也能增加他的size;注意:不能减少
应用场景:
在for循环插入多个元素,可能vector会多次分配空间,reserve只会分配一次,避免多次内存分配。resize确保操作符[]和at的安全性(不会越界)
vector<int> uprank_task;
uprank_task.clear();
uprank_task.reserve(4); //一次内存分配,避免多此内存分配
for(int i=0;i<4'i++){uprank_task.push_back(i);}
vector的元素类型为什么不能是引用
vector<T&> 引用有什么特征?引用定义必须初始化,初始化不能是空对象,切不可再修改。引用是别名,不是对象,没有实际地址,不能定义引用的指针,也不能定义引用的引用。
不能为引用分配内存。
push_back:不饿能赋值
vector引用类型不能进行有参构造
基于操作符[]和at,将会获取引用的引用,从而产生矛盾
list底层实现原理
list底层实现一个双向的循环链表。
类构成: class list:protected_List_base
_list_base_list_impl.list_node: _M_storage:存储具体的值
_M_next:指向下一位
_M_prev:指向上一位
构造函数:不管怎样初始化时都会构建一个空结点
迭代器:
deque的底层实现原理
目的:实现双端数组; 底层实现:双向开口的连续线性空间。可以在增加删除元素的时候,不用移动数据,比vector好。
底层双端数组的实现,是一个双向开口的 指针数组。每个元素致电给一个连续空间,连续空间容纳元素。迭代器记录时那个连续空间。 有两个迭代器: start finish
start迭代器的cur指向第一个连续空间的第一个元素
finish迭代器的cur指向最后一个连续空间的最后一个元素。
能够容纳的数组元素个数 =512/单个元素的大小。
初始化map:最小是8个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPJy0y5R-1688809378285)(C:\Users\ADMINI~1\AppData\Local\Temp\企业微信截图_16847327338369.png)]
两端开口可申请空间,每个元素都是连续空间。两端可增删。与vector有差异,删除不会引起数据的移动。
类构成:class deque:protected_Deque_base
_Deque_base_Deque_impl _M_map:指针数组
_M_map_size: _M_map容器
_M_start:记录map数组中首个连续空间信息
_M_finish:记录map数组的最后一个连续空间的
连续空间
_M_start和 _M_finish指向中间位置(指向的是连续空间),方便公平的想两端扩展空间()
push_back :当前连续空间够不够
map空间够不够 ----map不够则将生成一个新的map将旧的数据嵌入到新map中
pop_back: 删除最后一个结点,如果当前连续空间没有数据了,则释放该连续空间。
重点: 连续空间什么时候扩展,什么时候释放。几个迭代器的含义没使用等选择。map数据如何确定大小。这个固定的连续空间可以放多少个元素。
什么时候使用vector,list,以及deque?
vector: 优点:可以用[] 或at访问, 缺点:当在一个位置删除添加一个元素时,会移动数据,效率低。如果在vector中有大量的增加或删除操作都会引起数据的移动。
list: 有大量的删除操作,使用list较好,且他的空间利用率较高。但是也是需要内存的申请和释放。
deque:双端数组,在首尾增加删除效率高,支持[],at操作。
如果需要高效的直接访问(随机存取),且不在乎增删效率,用vector。
需要大量的插入删除,且不关心快速访问(随机存取),使用list;
如果需要快速的访问,且关心两端数据插入和删除,用deque;
priority_queue的底层实现原理
一句话:用堆来实现优先级队列-----大致有序
堆结构—完全二叉树:由左向右排列,中间不能为空二叉树。最后一层的最右一个不为空 的位置上进行增删操作的树。可用数组存储, 索引的数学映射关系:索引为n的结点, 左子节点索引为2n+1,右子节点索引为2n+2;只对数组的最后一个位置进行增删作
最大堆,最小堆: 只规定了父子之间的大小关系,不限定兄弟之间的大小关系;最大 堆,最小堆;
unordered_map的底层实现原理
一句话:基于散列表实现map
散列表:将kv对通过对key进行hash运算映射到一个hash表的具体位置
哈希碰撞:将多个不同的key映射到散列表同一个位置的行为。线性探查。开放寻址的方式。 拉链法等。
stl采用的就是拉链法。
描述冲突: 负载因子:实际存储的元素个数/散列表桶个数(桶个数=key)
负载因子>1时,将发起rehash
stl中的散列表是如何实现:依赖于hash_table数据结构
key:节点建的类型
value:节点数据类型
ExtractKey: 从value中取key的方法对象类型
Equal:判断key是否相等
节点类型:
数据定义:
指针数组:
数组长度(桶个数):
实际存储元素个数:
头节点:
rehash的策略对象
节点定义:
工作原理/工作流程:
迭代器的底层实现
5种类型:输入,输出,前向,双向,随机。
萃取:1萃取迭代器:模板,把iterator内部各个类型萃取出来
2将指针类型进行泛型特化
函数重载;选择不同的算法实现。
迭代器属性: 输入:istream_iterator: 只读且只可读一次,迭代器不能保存,迭代器传 递过程中,上个迭代器会失效。
输出:ostream_iterator:只写且只可写一次,迭代器不能保存,迭代器传 递过程中,上个迭代器会失效。
前向:forward_list,unordered_map,unordered_set:可读可写,且可多次
可保存
双向:list,map,set,multimap,multiset
迭代器不可保存,迭代器传递过程中,上个迭代器会失效。可正反 向遍历
随机访问:vector,deque
迭代器失效,连续和非连续存储容器的失效
容器类别: 序列型容器:vector deque queue
关联容器:set map multiset multimap
链表容器:forward_list list unordered
失效情况:
单独插入或删除: 插入:insert,emplace,push_back,push_front
删除:erase,pop_back, pop_front, clear
stl容器线程安全性
容器的内部实现已经固定,内部没有加锁,不能修改源码加锁。
背景知识:
解决方案:
加锁:
互斥锁—
读多写少用读写锁 —
并发插入优化:把插入行为转换为修改数据行为–提前分配好节点,构建节点间关系 resize解决扩缩容问题,reserver解决扩缩容 问题,未解决节点关系问题。
红黑树:提前拿到所有key,若对同一key并发修改问题时,仍需要加锁,但可以使用原子操作。
并发插入删除优化:list 生产消费模型 注意:deque不行,基于deque的其它容器也不行。
不加锁,避免加锁 提前分配好节点,并将数据分成多份,每个线程只操作专属那份数据
回答:内部是固定的不可改。 哪些容器会引起扩缩容:vector 会整个移动数据,queue,stack,priority_queue
扩缩容时,加锁会引起异常。
c++面向对象常见题型
c++三大特性
封装:
目的: 隐藏实现细节,实现模块化 。 访问权限
继承:
目的:无需修改原有类的情况下,通过继承实现功能的扩展
基类在子类中的最高权限: 理解 就是基类可以访问的权限,
权限继承: public: 类的属性保持不变 但是private在类中不可访问(基类中private对自己可见,在子类中不可见)。
protected:
private:
多态:
目的:一个接口多种形态,通过实现接口重用,增强可扩展性。
特性: 静态多态:函数重载
动态多态:通过虚函数重载
简述多态实现原理
静态多态: 函数重载,编译器确定
函数重载:函数的参数列表,参数的个数/参数的类型/参数的顺序 不同。 注意:不能通过返回值的类 型支持重载
原理: 函数名修饰,
编译过程:
预编译:把头文件中的函数声明拷贝到源文件中。
编译:语法分析,同时进行符号汇总(函数名)
汇编:生成函数名到函数地址的映射,便于通过函数名找到函数位置去执行函数
链接:将多个文件中的符号表汇总合并。
动态多态: 虚函数重写,运行时多态,
原理: 早绑定
晚绑定,若类使用virtual函数,则会为类生成虚表(一维数组,存放函数地址),类对象构造时会初始化该虚表指针。虚函数表指针是在构造函数中初始化的。
function,lambda,bind 之间的关系
std::function类模板 是什么: function是一个抽象函数参数以及函数返回值的类模板
抽象:把任意函数包装还曾一个对象
c++类型推导用法
类型:模板方法中的模板参数类型推导,auto ,decltype;
为什么要引入类型推导? c++强类型语言,编译器来处理类型推导,提升语言的编码效率。
auto:
原理:auto用于推导变量类型,通过强制声明一个变量的初始值。编译器会通过初始值进行推导类型。auto变量必须在定义时初始化,若定义多个变量,必须将这些变量为同一类型; 类型推导时,会丢失引用或cv语义,需要保留引用或cv语义,可用auto&;
万能引用auto&&,根据初始值的属性来判断是左值引用还是右值引用; auto不能推导数组类型,会推导指针类型。
c++14 auto可以推导函数的返回值类型。
应用: 尽量使用auto声明变量,除非影响可读性。 要标记一下; 使用容器时; 匿名函数返回值; 模板函数中,可节约模板参数类型。
decltype:
原理: 用于推导表达式的类型,只分析表达式类型而不参与表达式运算;
规则:exp是一个普通表达式,推导表达式
关键字override,final作用
C11引入的2个关键字: 为什么引入这2个关键字?
虚函数重写: 不能阻止虚函数进一步重写,
本意写一个新函数,错误重写虚函数(子类virtual关键字可省略);
本意重写基类虚函 数,但是签名不一致,在子类中重新构建一个新的虚函数。
类继承:不能阻止某个类进一步派生
class A{public: virtual void Func(){}}
class B public:A{public:public: virtual void Func(){}}
class c public:B{public: virtual void Func(){}} //不能阻止子类重写 还有可能重写虚函数写错名导致 有构建一个虚函数
override:能确保 签名一致 函数 virtual void Func() override {}
final: 指定虚函数不能派生类中的被覆盖,或某个类不能被派生。阻塞类进一步派生,阻塞函数进一步重写。
继承下的构造函数和析构函数执行顺序
单继承:成员类的按照顺序构造,析构按倒叙析构。类的构造依赖成员列的构造,基类比成员类依赖性更强。
classA{public:A(){cout<<"A";} ~A(){cout<<"`A";}}
多继承
虚函数表和虚函数表指针(ptr)的创建时机
虚析构函数的作用
智能指针的种类及使用场景
指针管理的困境:
资源释放了,指针没有置空: 野指针->一个指针指向资源
指针悬挂->多个指针指向同一个资源;
踩内存-> 指针没有置空,但是那块内存被其他对象持有
没有释放资源,造成内存泄漏;
重复释放资源,引发cordump;
如何解决?
智能指针采用RAII思想来自动化管理指针指向的动态资源的释放;
RAII主要利用了对象的生命周期来控制程序资源;只能指针利用类的构造函数和析构函数来管理资源。
智能指针种类: shared_ptr: 语义:共享所有权;资源没有明确的拥有者 原理:引用计数; (如:一个房间 有人时开灯,无人时,关灯)
场景: 1容器中管理指针 2资源通过函数传递(能够在出作用域自动析构)
使用规范:
使用shared_ptr来管理动态资源时,不要使用原来的裸指针;
构造时不要暴漏裸指针,尽量使用make_shared来构造智能指针;
不要通过get来操作裸指针;
不要使用一个指针构造多个智能指针对象;(如返回值是一个指针智能指针)
不要使用类对象指针(this)作为shared_ptr返回
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-by7PRfHW-1688809378286)(C:\Users\Administrator\AppData\Local\Temp\企业微信截图_16849923154647.png)]
class T{};
vector<T*> vec;
vec.clear(); //只是释放栈空间
vector<shared_ptr<T>> vec;
vec.clear(); //当没有应用时自动析构堆空间
int *p = new int();
shared_ptr<int> sp =shared_ptr<int>(p); //不要这么写
shared_ptr<int> sp = shared_ptr<int>(new int);//隐藏裸指针
shared_ptr<int> sp =make_shared<int>(); //推荐 弱引用
weak_ptr:
unique_ptr:独享所有权 ,
没有拷贝构造,
没有赋值运算符操作符,
仅提供了移动构造和移动赋值;
明确某个对象只有一个拥有者;
场景:
使用规范: 不支持拷贝,但可以从函数中返回一个unique_ptr 编译优化,1有移动构造调用移动构造; 有拷贝构造,调用拷贝构造;没有拷贝构造报错。
class T1{}
unique_ptr<T1> get_unique()
{
unique_ptr<T1> up;
return up;
}
int main(){
unique_ptr<T1> = get_unique();
}
{ int *p = new int;
delete p;
p =nullptr;}
{int *p = new int;
int *p1 =p;
int *p2 =p;
int *p3 =p;
int *p3 =p;
delete p; //多个指针指向一个内存 置空后 其他指针可能还在操作
p =nullptr;}
void function(int *p){
if() return ;
delete p;
}
void func(int *p){
func1(p); //深层嵌套
}
weak_ptr: 主要是解决指针被循环引用的问题;
class B;
class A{public: shared_ptr<B> spd; ~A(){} }
class B{public: shared_ptr<A> spa; ~B(){} weak_ptr<A> spa; }
int main(){
shared_ptr<A>sp1 = make_shared<A>();
shared_ptr<A>sp1 = make_shared<B>();
sp1->spb = sp2;
sp2->spa = sp1; //造成循环引用
return 0;
}
多线程下使用智能指针
需要考虑,避免悬空指针,
对于共享资源shared_ptr 要加互斥锁或其他同步机制保护资源访问和修改。
减少共享资源 。
注意资源所有权。
shared_ptr
c++模板
template<typename T> //T代表类型,使用函数是才会被确定
T max(T a,T b)
{
}
模板参数:
c++函数重载
类 &operator() (参数){}
C11用过哪些特性?
考察思路: 回答问题的层次
学习总结的思路
1语法糖:
关键字:
auto , decltype,
nullptr ,
final ,override,
constexpr
语法:
基于范围的for循环
function函数对象 bind ,lambda
目的:写代码更加便捷,更严谨 ,编译器做更多的事,
2stl容器:
array
forward_list
unordered_map
unordered_set
3智能指针:
shared_ptr,weak_ptr
unique_ptr
4多线程:
thread
mutex,lock_guard
condition_variable
atomic
5右值引用:
T&&:T类型
将亡值: 移动构造,移动拷贝构造
实现移动语义:std::move
实现完美转发:T&&:(T模板的T)万能引用
forward
动态库与静态库的区别
动态库需要定义加载路径;
生成方式: 静态库生成:
动态库生成:
链接方式: 静态链接:把静态库编译进目标文件
动态链接:程序运行时才去加载运行代码;地址无关代码技术 -fPIC
装载时重定位,只做语法检查。
空间占用: 静态库:会存在多个副本(内存和磁盘)
动态库:只有一个副本
使用方式:静态库,直接运行; 动态库:动态加载,程序环境需要指定动态库路经
库文件发生变更: 接口改变 都需要重新编译
接口实现改变: 静态库:都需要重新编译
动态库:只需要重新编译动态库
**左值引用与右值引用的区别,右值引用的意义
左值引用与右值引用的区别:左值引用是对左值的引用;右值引用是对右值的引用。
const左值引用能指向右值;局限是不能修改。右值引用通过std::move(v)可以 指向左值; 声明出来的左值引用和右值引用都是左值。
功能差异: 左值引用是避免对象的拷贝 :传参 函数返回
右值引用实现移动语义,实现完美转发;
右值引用的意义: 就是实现移动语义,实现完美转发;
什么是左值? 可以在等于号左边,能够取地址,有名;
变量名, 返回左值的函数调用,前置自增,前置自减,赋值运算或复合赋值运算 表达式,解引用表达式,
什么是右值? 只能在等号右边,不能取地址,没有具名;
右值分:纯右值,将亡值;
纯右值:字面值,返回非引用类型的函数调用,后置自增,后置自减,算数表 达式,逻辑表达式,比较表达式,
将亡值:c11新引入的右值引用相关的值类型,将亡值用于触发移动构造或移动 的赋值构造,进行资源转移,之后将亡值将调用析构函数。通过
理解移动语义,完美转发;
设计模式
面向对象有哪些设计原则
设计原则和设计模式的关系? 设计模式是由设计原则迭代产生
常见的设计原则:
**开闭原则:**一个类应对扩展开放,对修改关闭;–扩展 继承 组合
单一职责原则:一个类应该仅有一个引起他变化的原因;
里氏替换原则: 约束多态中虚函数的复写; 子类复写父类方法时,需要实现父类方法的职责,避免之前调用的父类方法地方出错。 因为设个方法时多态。
场景:多态中虚函数覆盖
目标:对扩展开放,约束扩展中的继承
接口隔离原则:不应强迫客户依赖它们不使用的方法
一个类对另一个类的的依赖应建立最小的接口
迪米特原则:最少知道原则→尽量降低类与类之间的耦合度 → 减少不必要的依赖
一个对象应对另外一对象保持最小的了解
目标: 高内聚:相近的功能放一个类中,不同的共嗯那个放不同类中 低耦合:类与类之间关系简单清晰,依赖越少越好
要求: 不该有直接依赖关系类之间不要依赖, 有依赖关系,尽量只依赖必要接口
依赖倒置原则; 高层不应依赖低层,两者都应依赖于抽象
抽象不应依赖具体实现,具体实现依赖于抽象。
为了解耦; 构建抽象— 不同实现细节
目标: 高内聚 低耦合
要求: 高层不依赖底层, 两者都依赖抽象,抽象不应依赖细节,细节依赖抽象
组合由于继承: 组合低耦合 继承高耦合
简述开闭原则,哪些原则与它相关,分别时什么关系?
一个类应该对扩展开放,对修改关闭。 稳定部分 变换部分。 扩展 继承 组合。
单一职责:一个类应仅有一个引起它变化的原因
里氏替换: 子类方法复写父类方法时,需要实现父类方法的职责,避免之前调用父类方法的地方出错
接口隔离: 不应强迫客户依赖他们不需要的接口:public protect privite,一个类对另一个类的依赖应建立在最小的接口上。
单例模式 多线程?
一个类仅有一个实列,提供一个该类的全局访问点;
//单线程的单列模式
在类内创建一个实列全局,后限制 构造函数私有 拷贝函数私有 重载函数私有(确保仅有一个实列)
静态成员变量(指针): 存储在静态全局区-程序退出会释放, 但是类对象在堆区不会被释放。 如果该对象有加载文件io 或socket io不会被正确释放。需要考虑该对象的合理释放。 借助 c++11 库函数 std::atexit();
class SingLeton{
public:
static SingLeton *GetInstance()
{
if(_instance == Nullptr)
_instance =NEW SingLeton();
std::atexit(Destructor); //程序退出时会自动调用该函数
return _instance;
}
privite:
static SingLeton *_instance; //类内定义
SingLeton(){}
~SingLeton(){}
SingLeton &operator =(const SingLeton &){}
SingLeton (const SingLeton &close){}
static void Destructor()
{
if(_instance != Nullptr) {delete _instance; _instance}
}
};
SingLeton *SingLeton::_instance =Nullptr;//类外初始化(初始化的写法)
SingLeton::GetInstance().函数方法; //单列使用
//程序在正常退时 调用指定函数 std::atexit(函数名);
//多线程下的单例模式
1.确保同时只能有一个线程来new该对象 - – 加互斥锁
静态成员函数中 只能访问静态成员变量。
多线程cpu对程序优化-1分配内存2调用构造函数3返回 赋值运算
优化成 132---- 如何避免 用到2个技术 原子操作 保证这个为原子操作 _instance =NEW SingLeton();
保证123执行完再return 由内存屏障来保证
将 _instance 原子变量来实现 static std::atomic<SingLeton *> _instance;
#include<mutex>
class SingLeton{
public:
static SingLeton *GetInstance()
{
//std::lock_guard<std::mutex> lock(_mutex); //互斥锁
if(_instance == Nullptr){
std::lock_guard<std::mutex> lock(_mutex); //互斥锁 有锁等待
if(_instance ==nullptr) //等待完 这时
_instance =NEW SingLeton(); //多线程环境下编译器对程序进行优化-1分配内存-2调用构造函数-3返回指针赋值 运算
std::atexit(Destructor);
}
return _instance;
}
privite:
static std::mutex _mutex;//互斥锁
static SingLeton *_instance; //类内定义
SingLeton(){}
~SingLeton(){}
SingLeton &operator =(const SingLeton &){}
SingLeton (const SingLeton &close){}
static void Destructor()
{
if(_instance != Nullptr) {delete _instance; _instance}
}
};
SingLeton *SingLeton::_instance =Nullptr;//类外初始化(初始化的写法)
std::mutex SingLeton::_mutex ;
内存阑珊
#include<mutex>
class SingLeton{
public:
static SingLeton *GetInstance()
{
SingLeton *temp = _instance.load(std::memory_order_relaxed);//保证原子性
std::atomic_thread_fence(std::memory_order_ac);
//std::lock_guard<std::mutex> lock(_mutex); //互斥锁
if(temp == Nullptr){
std::lock_guard<std::mutex> lock(_mutex); //互斥锁 有锁等待
if(temp ==nullptr) //等待完 这时
temp =NEW SingLeton(); //多线程环境下编译器对程序进行优化
std::atomic_thread_fence(std::memory_order_release);
_instance.store(temp, std::memory_order_relaxed);
//内存屏障
std::atexit(Destructor);
}
return _instance;
}
privite:
static std::mutex _mutex;//互斥锁
static SingLeton *_instance; //类内定义
SingLeton(){}
~SingLeton(){}
SingLeton &operator =(const SingLeton &){}
SingLeton (const SingLeton &close){}
static void Destructor(){
SingLeton *temp = _instance.load(std::memory_order_relaxed);/
if(temp != Nullptr) {
delete temp;
temp =nullptr;
_instance.store(temp, std::memory_order_relaxed); //通过temp 获取指针
}
}
};
//SingLeton *SingLeton::_instance =Nullptr;//类外初始化(初始化的写法)
std::atomic<SingLeton*> _instance;
std::mutex SingLeton::_mutex ;
一个类仅一个实列,提供该实列的全局访问点
1静态成员函数和静态全局变量 包含头文件的地方可以直接使用。
2一个类仅一个实列: 构造 析构 拷贝构造 赋值操作符 设为私有
3.new 创建在堆上,atexit();
多线程单列模式: 1.new之前 加互斥锁 然后再加一个判断
2.多线程环境下,编译器和cpu的优化(指令重排) --原子操作上加内存屏障
acc -限制上面的代码不能移到下面去
recc -限制下面的代码不能移到上面去
每个地方说出为什么? 显示基础扎实
单列的使用: SingLeton::GetInstance.方法; //这样使用
代理模式? 应用场景?
定义:为其他对象提供一种代理以控制对这个对象的访问。
场景: 客户对象通过代理控制真实对象。
应用: 保护代理:
远程代理:两个进程间 互相调用对象。 rpc
虚代理:解决一类问题。 如:加载资源较费时,只有在使用时再去加载(延时加载的作用)
功能接口: 真实类 和 代理类 都需要继承该功能接口, 代理类组合真实对象类的基类指针。
客户类: 通过访问代理类操作真实对象类。
什么是工厂模式?什么是抽象工厂?应用场景是什么?
定义一个用于创建对象的接口,让子类来实现接口的实现。
场景:解决创建过程比较复杂期望对外隐藏这些细节。
链接池,线程池: 用户只想获得这个连接对象(将连接池线程池封装成工厂模式)–(连接的细节–如何连接数据库验证密码等初始化数据等都不去关注)
线程池也是: 不关注如何创建内部如何调优等。 只由一个人关注实现,其他使用者只关注他的接口;
有多个参数决定创建
创建对象有复杂的依赖关系
符合的设计原则:依赖倒置:接口的实现 延迟到子类去实现
面向接口:只关注提供哪些功能,不关注具体实现
具体的实现: 接口
子类继承接口实现他(由子类实现他)
class Shoes{public: virtual void Show()=0;
virtual ~Shose(){}
};
class Nick: public Shose{public: void Nick(){}};
class Adides :public Shose{public: void Adides(){}};
class ShoesFoctory{public:
Shose *CreateShoes(int type){
switch(type){
case NICK:
return new Nick;
break;
case ADIDES:
return new Adides;
break;
}
};
};
int main()
{
ShoesFoctory shoesFactory; //构建工厂对象
Shose *pNikeShoes = shoesFactory.CreateShoes(NICK);//使用接口
if(pNikeShoes ==null)
{}
}
简单工厂模式: 抽象类 具体类 工厂类
抽象工厂模式: 抽象类 具体类 抽象工厂类 具体工厂类
总结: 一个工厂生产一类产品,不需要修改工厂类,只需要增加新的具体工厂类,不需要修改工厂类
实现导出数据的接口,数据格式包括xml,json,文本格式txt,后面还可能扩展execel格式csv格式
class IExport{ //抽象类
public:
virtual bool Export(const std::string &data) = 0;
virtual ~IExport(){}
};
class ExportXml :public IExport{
public:
virtual bool Export(const std::string &data){ return true;}
};
class ExportJson :public IExport{
public:
virtual bool Export(const std::string &data){ return true;}
};
class ExportTxt :public IExport{
public:
virtual bool Export(const std::string &data){ return true;}
};
//在使用工厂对象的时候--工厂对象需要一个接口 虚类
class IExportFactory{
public:
};
什么是抽象工厂模式?
定义:提供一个接口,让该接口负责创建一系列“相关或者互相依赖的对象”,无需指定他们具体的类。
(创建多个对象,且有相互依赖关系,)
场景: 解决创建过程比较复杂期望对外隐藏这些细节 且多个相关性或者依赖的多个对象。
使用者只需要知道如何使用,而不需要知道具体实现。
什么是代理模式?应用场景?
定义:为其他对象提供一种代理以控制对这个对象的访问。
场景: 客户对象通过代理控制真实对象。
应用: 保护代理
虚代理
远程代理
客户对象 访问真实对象
class ISubject{
public:
virtual void Handle()=0;
virtual ~ISubject(){}
};
class RealSubject :public ISubject{
public:
virtual void Handle(){}
};
class Proxy1 : public ISubject{
public:
Proxy1(ISubject *subj);
};
什么是装饰器模式?应用场景?
定义:通过组合的方式动态的给一个对象增加一些额外的职责。
强调最终的效果。更具体的组合顺序无关,每一个职责是独立的功能,职责之间没有依赖关系
本质: 动态的组合职责
设计原则: 组合优于继承
什么是组合模式?应用场景是什么?
什么是责任链模式?应用场景是什么?
什么是模板方法? 应用场景是什么?
什么是策略者模式? 应用场景是什么?
什么是观察者模式? 应用场景是什么?
操作系统
进程与线程区别?
对于操作系统来说:
本质区别:进程是资源覅都以及分配的基本单位;线程是cpu调度的基本单位
所属关系:一个线程属于一个进程;一个进程下可有有多个线程
地址空间:线程没有自己独立的虚拟地址空间(线程有栈,程序计数器,本地存储器等),进程有自己独立的虚拟地址空间
内存: 系统为每个进程分配内存空间; 系统不会为线程分配内存,线程使用的资源来自与作书的进程资源
并发性: 单cpu进程的并发性较低;
线程的并发性较高; 切换效率,都会涉及上下文的切换,进程切换效率低,线程切换效率较高; 对于单cpu系统会将cpu运行时间划分为多个时段,然后将时间段分配个各个线程执行。
健壮性:多个进程间互补影响;
操作系统中进程与线程的切换过程
进程由哪几部分构成? task_struct
进程的地址空间: 代码—
描述整个系统调用过程?
系统调用是什么? 系统调用使内核给用户程序提供的编程接口
为什么? 内核具有最高权限,可直接访问所有资源,用户只能访问受限资源,不能直接访问内存,网络,磁盘等硬件资源
粗略流程:应用程序
函数库
系统调用
内核
中断
后台进程有什么特点?
进程间通信有哪几种方式?
操作系统中进程调度策略有哪几种?
cas是一种什么样的同步机制?
cpu是怎么执行指令的?
原理:利用硬件的原子操作指令 cas操作三个步骤: 比较 写入 返回结果
用户态和内核态的区别?
内存管理有哪几种方式?
malloc是如何分配内存的?
页面置换算法有哪些?
谈谈cpu cache一致性工作原理?
写文件是进程宕机,数据丢失吗?
磁盘调度算法有哪些?
mysql数据库
mysql中b+数一页16k
redis数据库
网络编程
分布式理论
需要知道如何使用,而不需要知道具体实现。
什么是代理模式?应用场景?
定义:为其他对象提供一种代理以控制对这个对象的访问。
场景: 客户对象通过代理控制真实对象。
应用: 保护代理
虚代理
远程代理
客户对象 访问真实对象
class ISubject{
public:
virtual void Handle()=0;
virtual ~ISubject(){}
};
class RealSubject :public ISubject{
public:
virtual void Handle(){}
};
class Proxy1 : public ISubject{
public:
Proxy1(ISubject *subj);
};
什么是装饰器模式?应用场景?
定义:通过组合的方式动态的给一个对象增加一些额外的职责。
强调最终的效果。更具体的组合顺序无关,每一个职责是独立的功能,职责之间没有依赖关系
本质: 动态的组合职责
设计原则: 组合优于继承
什么是组合模式?应用场景是什么?
什么是责任链模式?应用场景是什么?
什么是模板方法? 应用场景是什么?
什么是策略者模式? 应用场景是什么?
什么是观察者模式? 应用场景是什么?
操作系统
进程与线程区别?
对于操作系统来说:
本质区别:进程是资源覅都以及分配的基本单位;线程是cpu调度的基本单位
所属关系:一个线程属于一个进程;一个进程下可有有多个线程
地址空间:线程没有自己独立的虚拟地址空间(线程有栈,程序计数器,本地存储器等),进程有自己独立的虚拟地址空间
内存: 系统为每个进程分配内存空间; 系统不会为线程分配内存,线程使用的资源来自与作书的进程资源
并发性: 单cpu进程的并发性较低;
线程的并发性较高; 切换效率,都会涉及上下文的切换,进程切换效率低,线程切换效率较高; 对于单cpu系统会将cpu运行时间划分为多个时段,然后将时间段分配个各个线程执行。
健壮性:多个进程间互补影响;
操作系统中进程与线程的切换过程
进程由哪几部分构成? task_struct
进程的地址空间: 代码—
描述整个系统调用过程?
系统调用是什么? 系统调用使内核给用户程序提供的编程接口
为什么? 内核具有最高权限,可直接访问所有资源,用户只能访问受限资源,不能直接访问内存,网络,磁盘等硬件资源
粗略流程:应用程序
函数库
系统调用
内核
中断
后台进程有什么特点?
进程间通信有哪几种方式?
操作系统中进程调度策略有哪几种?
cas是一种什么样的同步机制?
cpu是怎么执行指令的?
原理:利用硬件的原子操作指令 cas操作三个步骤: 比较 写入 返回结果
用户态和内核态的区别?
内存管理有哪几种方式?
malloc是如何分配内存的?
页面置换算法有哪些?
谈谈cpu cache一致性工作原理?
写文件是进程宕机,数据丢失吗?
磁盘调度算法有哪些?
mysql数据库
mysql中b+数一页16k