一、c11部分新特性
智能指针
智能指针作用:
C++程序设计中使用堆内存是非常频繁的操作,使用普通指针容易造成内存泄漏、二次释放等问题。C++11中引入了智能指针的概念。智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏
为什么智能指针可以像普通指针那样使用???
因为其里面重载了 * 和 -> 运算符, * 返回普通对象,而 -> 返回指针对象。
auto_ptr:
auto_ptr 是c++ 98定义的智能指针模板,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存!
auto_ptr 被C++11抛弃的主要原因:
a、复制或者赋值都会改变资源的所有权。
b、在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值。
unique_ptr:
unique_ptr是auto_ptr的一个改良版,不能赋值也不能拷贝,保证一个对象同一时间只有一个智
能指针。
shared_ptr:
shared_ptr可以使得一个对象可以有多个智能指针,当这个对象所有的智能指针被销毁时就会自
动进行回收。(内部使用计数机制进行维护)
weak_ptr:
weak_ptr是为了协助shared_ptr而出现的,只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 他的构造或者析构不会改变计数器,它不能访问对象,只能观测shared_ptr的引用计数,防止出现死锁。
右值引用
右值引用和左值引用的区别:
左值持久,右值短暂,左值引用绑定到有确定存储空间以及变量名的对象上,表达式结束后对象依然存在;右值引用绑定到要求转换的表达式、字面常量、返回右值的表达式等临时对象上,赋值表达式结束后就对象就会被销毁。
引入右值引用原因:
替代需要销毁对象的拷贝,提高效率
lambda表达式
1.捕获列表。在C ++规范中也称为Lambda导入器, 捕获列表总是出现在Lambda函数的开始处。
2.参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。
3.异常说明。用于Lamdba表达式内部函数抛出异常。
4.返回类型。 追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值(或者明确返回类型)的时候也可以省略。
5. lambda函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
并发编程
并发与并行的区别:
并发:同一时间段内可以交替处理多个操作
并行:同一时间段内同时处理多个操作
并发的两种模式:
1.多进程并发:多个进程独立地运行,它们之间通过进程间常规的通信渠道传递讯息(信号,套接字,文件,管道等),这种进程间通信不是设置复杂就是速度慢
2.多线程并发:线程就像轻量级的进程,每个线程相互独立运行,但它们共享地址空间。在多线程编程中,我们必须确保每个线程锁访问到的数据是一致的。
二、c调用堆栈和内存布局
内存布局
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量值等,其操作方法类似数据结构中的栈。
2、堆区(heap):一般由程序员分配释放,与数据结构中的堆毫无关系,分配方式类似于链表。
3、内存映射段:存放 静态库/动态库。
4、数据段:存放全局变量,静态变量
bss段:存放未经过初始化的全局变量,静态数据
5、程序代码区(代码段):存放可执行代码
栈与堆的比较
栈区:栈区一般存放函数体的局部变量、函数调用期间的所有参数压栈、函数的返回值
1、申请方式
stack:系统自动分配,如声明int a;系统自动在栈空间中为a开辟空间
heap:程序员申请,并指明大小,c中的malloc,如charp=(char)malloc(10);
C++中的new运算符:如int*p2=new int(10);
注意:p和p2本身是在栈中的,但他们指向的地址是堆空间
2、系统响应
栈:只要系统剩余空间大于申请空间就能申请,否则报错:栈溢出
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,对于大多数系统,首地址处会记录这块内存空间中本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3、申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间受限于计算机系统中有效的虚拟内存,比较灵活,也比较大。
4、申请效率
栈由系统自动分配速度较快,堆由new分配速度较慢,且容易产生内存碎片,但使用方便
三、STL一级、二级空间配置器
空间配置器概念
空间配置器是操作系统开辟的一大段内存空间。STL需要扩容申请内存时,就从空间配置器中申请,不需要再经过操作系统。并且,它还能回收释放的空间,供下一次使用。一个进程中有一个空间配置器,进程中所有容器需要的空间都到对应空间配置器申请。进程终止,对应空间配置器空间释放。
优点
a、提高效率 b、避免内存碎片
空间配置器原理
空间配置器有两级结构,一级空间配置器是用来处理大块内存,二级空间配置器处理小块内存。SGI-STL规定以128字节作为小块内存和大块内存的分界线。
为什么这样区分成两级?
因为STL容器,一般申请的都会是小块的内存,二级空间配置器,主要是管理容器申请空间和释放的空间。
如果用户申请的空间直接大于的128字节直接找的是一级空间配置器申请空间。
一级空间配置器
一级空间配置器原理很简单,直接是对malloc和free进行了封装,并且增加了C++中的申请空间失败抛异常机制。
主要的作用是:向操作系统申请内存,申请失败会抛异常。
为什么不直接用C++的new和delete,因为这里并不需要调用构造函数和析构函数。
二级空间配置器
二级空间配置器专门负责处理小于128字节的小块内存。
SGI-STL采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费,采用哈希桶的方式来提高用户获取空间的速度和高效管理。
内存池技术
内存池就是,先申请一块较大的内存块做为备用,当需要内存时,直接从内存池中取内存,当内存池中内存不够时,使用一级空间配置器,向内存中申请大块空间。当用户不用申请的空间时,直接归还内存池。这样就避免了频繁向系统申请小块内存找出的效率低,内存碎片的问题。
当用户释放该空间时,并不知道这块空间应该放在内存池的什么位置,在STL配置器中,使用哈希桶的技术来解决这一问题。
哈希桶技术
为什么不用链表来管理归还的空间?
因为用户申请空间,查找合适的内存块时,效率低。
为什么向上对齐到了8字节的整数倍?
我们的内存块是像链表一样连接起来的,这样就必然需要指针来维护, 32位平台下指针4字节,64位平台下指针8字节,所以内存块最小也要能存放一个指针。但是这样造成了内存碎片问题
大致流程:
容器进行扩容,如果申请的空间是大于128字节,直接向一级空间配置器申请。如果小于128字节,先查找哈希桶对应大小位置是否为空,不为空,直接从该位置申请空间,如果该位置为空,向内存池申请。当内存池空间不够了会直接向OS申请一大块空间
内存碎片问题
外碎片问题:
由于频繁申请小块内存,导致被申请的内存块不连续,如果下一次需要申请一大块内存,内存空间够,但是由于不连续,导致申请不出来。
内核针对大量申请在堆上小块内存导致碎片化的问题,是用来slab分配器来解决。结构类似二级空间配置器的哈希结构
内碎片问题:
内碎片问题:给的内存数比实际要的内存数多,导致空间浪费。二级配置器切割内存块向上对齐8的整数倍,就造成了内碎片问题
四、八大排序总结
五、何为嵌入式系统
用于控制、监视或者辅助操作机器和设备的装置。是以应用为中心,以计算机技术为基础,软件硬件可裁剪,适用于对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。