1、空类有多大,都有什么函数
空类同样进行了实例化,所以内存中会有一个地址存储。所以是一个字节。
构造,析构,拷贝构造,赋值运算符,取地址运算符,const取地址运算符
2、struct内存对齐
分为三步,首先,每个成员变量的偏移量必须是前一个数据成员变量偏移量的整数倍。
整个struct结构体长度必须是最大数据长度的最小整数倍。
如果内置的#progam pack(n),则需是这个n的最小整数倍
3、内存泄露的情况
就是内存空间使用后未归还。常见动态存储分配的空间使用完毕后未释放,一直占用着。用完malloc和new,不free和delete。
比如:单个类中的使用new创建了对象,但析构的时候没有delete。
创建了数组,但是没有用delete数组的形式,而是用delete删除。
创建了对象的指针数组,如果只是用delete删除数组空间,而没有释放每个指针指向的空间。
两次释放相同内存。比如缺少拷贝构造,在拷贝构造的时候,进行浅拷贝,仅仅是拷贝指针。
基类的虚构函数不是虚函数。
常见内存泄露:
单例对象造成的内存泄露:
因为单例对象是在应用使用期间一直存在,某个对象已经不再使用,但单例对象还持有这个对象的引用,就会出现内存泄露。
4、指针和引用的区别
指针和引用都是地址的概念。
指针指向内存,引用是某块内存的别名。
相同的地方:指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
指针是一个实体,引用是个别名。
引用在定义的时候初始化一次,之后不可变。而指针可变。
引用没有const,而指针可以有const;
sizeof(引用)得到的是指向变量的大小,sizeof(指针)得到的是指针本身的大小。
指针的自增和引用的自增不同。
引用是类型安全的,指针不是。引用包括类型检查。
声明引用必须初始化。引用不能指向NULL
5、野指针产生与避免
野指针:访问一个已销毁或者访问受限的内存区域的指针。野指针不能通过判断是否为NULL来避免。
野指针的形成:
1、指针定义的时候未被初始化。可以初始化为NULL;
2、指针超过变量作用域范围,常见数组越界等。
3、使用malloc创建的释放后未指向NULL
6、27、四种智能指针及底层实现:auto_ptr、unique_ptr、shared_ptr、weak_ptr
auto_ptr采用的剥脱的方式。
unique_ptr,一个时间只有一个指针可以访问,禁止了拷贝和赋值。但可以使用move转移所有权。
unique_ptr,一个时间只有一个指针可以访问,禁止了拷贝和赋值,但可以通过move转移所有权。
unique_ptr指针本身的声明周期从unique_ptr指针创建开始,直到离开作用域。
shared_ptr,内部有一个计数变量。但是无法解决循环引用的情况。所谓循环引用就是死锁。a的成员用到了b,b的成员用到了a。这样释放的时候无法释放。
shared_ptr<T> p2;
p2.reset();
p2.reset(new class());//这里面只能放new class
shared_ptr的reset调用,使用reset可以使指针数量减一,同时p2的引用数量变为0;如果reset里面有新的变量,则原来的变量减一,新的加一。
shared_ptr线程不安全
weak_ptr是配合shared_ptr使用,使用weak_ptr指向shared_ptr,原shared_ptr引用数量不会增加。他可以从shared_ptr或者weak_ptr构造而来。
weak_ptr也可以通过lock()返回一个shared_ptr;
28、shared_ptr中的循环引用怎么解决?(weak_ptr)
shared_ptr存在的问题
1、线程安全问题
shared_ptr有两个变量,所以拷贝和赋值都不是原子操作。
引入互斥量来解决。
2、循环引用问题
当a指向的对象中个引用了b,而b指向的对象又引用了a。这样的话,会出现a和b的计数都是2。析构的时候count值减一,无法释放,因为计数为0才析构。
通过weak_ptr解决。
最终a对象引用的是b的weak_ptr。而weak_ptr,只是获得一个shared_ptr的观测权。它的指向不会增加shared_ptr堆区的count计数。所以在重复引用的释放的时候,shared_ptr计数会变为0;
而weak_ptr在第一个shared_ptr析构的时候,自己维护的两个观测计数为减1,但是不会析构,等到第二个对象析构的时候,才会变为0,释放计数资源。
weak_ptr可以通过一个lock()获得一个实例对象。
8、STL迭代器失效
对于vector来说,插入的时候,如果容量不发生变化的情况下,在插入位置之前的迭代器会失效。
erase的时候,erase之后的都会失效,但erase可以返回删除之后的原先位置的新的迭代器。
对于list的话,只有删除的位置会失效。
对于map和set的话,插入不会失效,但是删除的话,只有原来位置的迭代器会失效。同样可以返回新的原来位置的迭代器。
而对于unordered_set和unordered_map,如果插入的时候不触发容量变化,迭代器不会变化。删除的时候则不会失效。
当new调用内存不够的时候,可以通过new handler处理。
重载、覆盖、隐藏区别
重载就是函数同名,但是参数不同。
重写就是覆盖,就是子类中对父类的一个函数重新定义。
隐藏就是子类的同名函数会隐藏父类同名的函数,默认。要调用父类的同名函数可以通过加作用域来实现。
静态库和动态库区别
程序生成的过程,预编译,编译,汇编,链接。
而库分为两种,一种是静态库(lib,a)一种是动态库(dll,so)
静态库就是在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件。
静态库对函数库的链接是在编译时期完成的,程序运行时与函数库无关系,移植方便,但是浪费空间和资源,因为所有相关的目标文件与牵扯到的函数库被链接合成一个可执行文件。
空间浪费是静态链接的一个文件,另外如果一个库更新了,那么可执行文件都要重新编译,链接。
动态库在程序编译的时候不会连接到目标代码中,而是在程序运行时才被载入。动态库在程序运行时才载入。所以避免了空间浪费,也解决了静态库读程序的更新。
总结:
动态库把一些库函数的链接载入推迟到程序运行时期。
可以实现进程之间的资源共享。
将一些程序升级变得简单。
4.为什么static成员一定要在类外初始化?
这是因为被static声明的类静态数据成员,其实体远在main()函数开始之前就已经在全局数据段中诞生了!其生命期和类对象是异步的,(而且静态语意说明即使没有类实体的存在,其静态数据成员的实体也是存的)这个时候对象的生命期还没有开始,如果你要到类中去初始化类静态数据成员,让静态数据成员的初始化依赖于类的实体,,那怎么满足前述静态语意呢?难道类永远不被实例化,我们就永远不能访问到被初始化的静态数据成员吗? 所以为了满足C++的static语意,static成员一定要在类外初始化!
13、C/C++内的malloc内部实现
当开辟的空间小于128k时调用brk()函数,系统调用函数brk()后,移动指针_enddata。将这个指针向高地址推。
当开辟的空间大于128k的时候,调用mmap函数。系统调用这个函数在虚拟地址空间找一块空间来开辟。
空的struct占多大?
struct和class在c中是0个字节,在C++中是1个字节。
左值和右值
左值是既可以出现在等号左边也可以出现在等号右边。而右值只能出现在等号右边。右值一般是不能取址的常量后者临时对象。
STL中sort排序的原理?
会根据不同的情况选择不同的算法,默认是使用快排,分段递归。当分段后的数据量小于某个阈值的时候,会使用插入排序。当递归深度较高的时候,或者接近快排最坏的情况时,使用堆排序。
14、类对象的构造顺序,析构顺序
析构和构造相反。
析构是派生类的析构,对象成员析构,基类析构
为什么析构是虚函数,是因为多态在实现的时候,使用基类的指针指向子类的对象,如果不是虚函数,那么会只析构基类,不析构子类。
15、有哪些函数是不能为虚函数?
1、不能被继承的
2、不能被重载的
具体来言,有构造函数。多态是运行的时候不确定对象类型,而构造函数的意义在于编译的时候就确定到底是什么类型。这两个不相容。
静态成员函数和内联函数。因为这两个是编译时确定的,而虚函数是运行时确定的。
普通函数和友元函数是不能被继承的。
16、C++的右值引用?作用是什么?
右值引用主要是为了移动语义和完美转发。也就是移动构造和移动赋值。
右值引用延长了右值的生命周期,并且可以修改
而在移动构造的时候,因为右值一般是临时对象,所以构造的时候不需要先拷贝再删除,只需要从旧对象移动到新对象即可。
右值引用的声明和左值引用一样,右值引用必须进行初始化操作。且只能使用右值进行初始化。
完美转发,就是函数模板可以将自己的参数完美转发给内部调用函数,所谓完美就是不仅能准确转发参数的值,还能保证被转发参数的左右值属性不变。
具体实现的时候,只要函数模板的参数为T&&即可,因为会通过引用折叠。
什么是完美转发,它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
17、C++的lambda表达式,传值方式?
lambda表达式可以方便的使用匿名函数。
lambda可以通过方括号捕获外部的变量。
可以通过mutable来实现修改捕获变量的值。因为正常情况下lambda是一个常函数
lambda参数有三个限制
1、参数表不能有默认参数
2、不支持可变参数
3、所有参数必须有参数名
18、C++11新特性
1,auto关键字,变量类型自动推导
decltype表达式类型自动推导
2.nullptr。NULL在C++中就是0。而nullptr保证在任何语境下都是空指针。
3、范围for语句,可以直接遍历元素
4、Lambda表达式
5、智能指针
6、右值引用
7、列表初始化
8、并发 mutex pthread lock condition_variable
atomic原子类型
9、显示重写(覆盖)override和final
10、stl方面
array和forward_list,unordered_map和unodered_set
元组类型
11、模板方面
可变参数,直接在T后面加…
表示一个包含0到任意个模板参数的参数包
1)声明一个包含0到任意个模板参数的参数包
2)在模板定义得右边,可以将参数包展成一个个独立的参数
C++14新特性:
lambda表达式中的变量可以是auto类型。
函数返回值可以auto
make_unique;
C++11中只有make_shared;
C++17
constexpr关键字
结构化绑定
内联变量
折叠表达式
constexpr lambda表达式
命名空间嵌套
C++20
概念、范围、协程和模块