vector中v[i]和v.at(i)的区别
v[5]; //A v.at[5]; //B 如果v非空,A和B没有任何区别。如果v为空,B会抛出std::out_of_range异常。 c++标准不要求vecor<T>::operator[]进行下标越界检查,原因是为了提高效率。如果需要下标越界检查,使用at。但性能会受到影响,因为越界检查增加了性能开销。
vector扩容原理
新增元素:vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素;
对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了;
初始时刻vector的capacity为0,塞入第一个元素后capacity增加为1;
不同的编译器实现的扩容方式不一样,VS2015中以1.5倍扩容,GCC以2倍扩容
哪些函数不能成为虚函数?
不能被继承的函数和不能被重写的函数。
1.普通函数
2.友元函数
3.构造函数
4.内联成员函数
5.静态成员函数
溢出
1.栈溢出(栈的大小通常是1M-2M)
栈溢出是指函数中的局部变量造成的溢出(注:函数中形参和函数中的局部变量存放在栈上)
栈溢出包含两种情况:1.分配的的大小超过栈的最大值,2.分配的大小没有超过最大值,但是分配的buff比接收buff小(buff:缓冲区, 它本质上就是一段存储数据的内存)
注意:调试时栈溢出的异常要在函数调用结束后才会检测到,因为栈是在函数结束时才会开始进行出栈操作
栈溢出的解决办法
如果是超过栈的大小时,那就直接换成用堆;如果是不超过栈大小但是分配值小的,就增大分配的大小
堆栈溢出一般由什么导致?
没有回收垃圾资源
什么是平衡二叉树?
左右子树都是平衡二叉树,且左右子树的深度差的绝对值不大于1
c++ 11 新特性
1.nullptr :引入了 nullptr 关键字,专门用来区分空指针、 0 。 nullptr 的类型为 nullptr_t ,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较
2.auto 类型推导:最为常见而且显著的例子就是迭代器, auto 不能用于函数传参
3.decltype :从表达式的类型推断出要定义的变量类型
4.Lambda 表达式
(1)
(2) 更重要的应用是其可以用于函数的参数,通过这种方式可以实现回调函数
5.std::array 保存在栈内存中,相比堆内存中的 std::vector ,我们能够灵活的访问这里面的元素,从而获得更高的性能。 std::array 会在编译时创建一个固定大小的数组
6. 智能指针
为什么要使用智能指针?
使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。
unique_ptr 保证同一时间内只有一个智能指针可以指向该对象。
unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4 ; //#5
p4 = p3;// 此时会报错!!
程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做 :
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You"));// #2 allowed
shared_ptr 多个智能指针可以指向相同对象,
use_count()来查看资源的所有者个数
调用 release()时,当前指针会释放资源所有权,计数减一
unique 返回是否是独占所有权( use_count 为 1)
get 返回内部对象 ( 指针 )
weak_ptr 是一种不控制对象生命周期的智能指针 , 它指向一个 shared_ptr 管理的对象 . 它只可以从一个shared_ptr 或另一个 weak_ptr 对象构造 ,weak_ptr 是用来解决 shared_ptr 相互引用时的死锁问题 .shared_ptr 可以直接赋值给它,它可以通过调用 lock 函数来转换成 shared_ptr 。
静态函数和虚函数的区别
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定(编译器在编译阶段不知道要调用哪个方法)。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销。
基类为什么需要虚析构函数?
防止内存泄漏。假如没有虚析构函数,释放一个由基类指针指向的派生类对象时,不会触发动态绑定,
则只会调用基类的析构函数,不会调用派生类的。派生类中申请的空间则得不到释放导致内存泄漏。C++ 默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。
C++类内可以定义引用数据成员吗?
可以, 1. 必须通过成员函数初始化列表初始化。 2. 初始化后的引用变量所占用的内存空间和普通变量相同
C++源文件从文本到可执行文件经历的过程?
预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。
编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件
汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件
链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件
孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init 进程 ( 进程号为 1) 所收养,并由 init 进程对它们完成状态收集工作。
僵尸进程
一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。僵尸进程是一个进程必然会经过的过程:这是每个子进程在结束时都要经过的阶段。
危害:
如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。
brk 系统调用和 mmap 系统调用的作用
Malloc 在申请内存时,一般会通过 brk 或者 mmap 系统调用进行申请。其中当申请内存小于 128K 时,
会使用系统函数 brk 在堆区中分配;而当申请内存大于 128K 时,会使用系统函数 mmap 在映射区分配。
内存泄漏的分类
1. 堆内存泄漏 ( Heap leak )。对内存指的是程序运行中根据需要分配通过 malloc,realloc new 等从堆
中分配的一块内存,再是完成后必须通过调用对应的 free 或者 delete 删掉。如果程序的设计的错误导致
这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生 Heap Leak.
2. 系统资源泄露( Resource Leak )。主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET
等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
解决内存泄露的方法:智能指针和 vilgrind
32 位,64 位系统中,各种常用内置数据类型占用的字节数?
C++不能重载的操作符
原码,反码,补码
正数:原码、反码和补码都一样;
负数:
原码转换为反码:符号位不变,数值位分别“按位取反”
原码转换为补码:符号位不变,数值位按位取反,末位再加1
补码转换为原码:符号位不变,数值位按位取反,末位再加1
派生类和基类
1.派生类对象赋给基类对象;2.派生类对象可以初始化基类引用;3.对生类对象的地址可以赋给基类指针
构造函数
构造函数是系统自动调用的(创建对象时,系统自动调用构造函数);转换构造函数是单参数的构造函数,它可以将一个指定类型的数据转换为类的对象
深拷贝和浅拷贝
【浅拷贝】是增加了一个指针,指向原来已经存在的内存。
【深拷贝】是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。
浅拷贝存在的问题:在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误
编译系统在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,进行的是浅拷贝
在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
例1
struct fields{
unsigned short a:4;
unsigned short b :5;
unisgned short c:7;} test
int i=*((short *)&test) 取从地址&test开始两字节(short占两字节)(一字节=8位)的内容转化为short型数据
变量后面加:然后加数字表示位域,也就是按位存放,这是计算机为节省空间的一种方式
例2
1.int *p1=new int[10],2.int *p1=new int[10]() 1申请的空间里是随机值,2申请的空间里面的值已初始化
例3
typedef struct list_t{
struct list_t *next;
char data[0];
}list_t
data[0]不占空间,方便管理内存缓冲区,减少内存碎片化
例4
char *p1="12345"; char *p2=(char *)malloc(10);
p1,p2都存在栈中(指针本身存在于栈中),p1指向的对象存在于常量区,p2指向的对象存在于堆中,堆和栈在内存中的生长方向相反,堆的内存地址向上,栈的内存地址向下
例5:怎么做到对象不再栈上创建
编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。因此,将析构函数设为私有,类对象就无法建立在栈上了
例6
typedef 在编译的时候处理,define在预编译的时候处理
例7
typedef char *String_t 和 #define String_d char * 同时定义多个变量时:
String_t a,b
String_d c,d a,b,c是char* d是char
例8
结构体及结构体变量的初始化:传统的依次初始化每一个成员;默认初始化第一个成员,不能这样初始化,eg:p={,"age"}
例9
'\0':ASCII为0的字符,在内存中实际表示为0
例10
一般情况下:小循环放外面,大循环放里面,但是如果循环里面是数组,如果数组较长,跨页较多,会导致速度慢
例11
空类:编译器会自动生成:默认构造函数、析构函数、拷贝构造函数、赋值函数
例12
char str[][10]={"123","456"},*p=str; p指向这个数组的首指针,p+10指向数组第二维的首指针
来源:CSDN
作者:MrLi1
链接:https://blog.csdn.net/huanghundechenxi/article/details/104142072