个性化网站定制,郑州惠济区建设局网站,惠州 家具 网站上线,制作宣传片拍摄公司第五章 提高类型安全
5.1 强类型枚举
5.1.1 枚举#xff1a;分门别类与数值的名字
具名枚举类型一般声明类似#xff1a;enum Gender { Male, Female }。
匿名枚举类型可以使用三种方式实现#xff1a;
第一种方式时宏#xff0c;比如
#define Male 0
#define Femal…
第五章 提高类型安全
5.1 强类型枚举
5.1.1 枚举分门别类与数值的名字
具名枚举类型一般声明类似enum Gender { Male, Female }。
匿名枚举类型可以使用三种方式实现
第一种方式时宏比如
#define Male 0
#define Female 1
宏的弱点在于其定义的知识预处理阶段的名字会干扰正常代码。
第二种方式时匿名的enum比如enum { Male, Female};
c中更受推荐的是第三种方式静态变量例如const static int Male 0;
静态变量能够得到编译时检查作用域局限于文件内但是会在目标代码中产生实际的数据相比而言匿名的枚举似乎更好用。
5.1.2 有缺陷的枚举类型
c/c的enum有个奇怪的设定就是具名enum类型的名字和enum的成员的名字都是全局可见的。 上面例子中的两个General都是全局的名字因此编译器会报错。解决的办法是将它们声明在不同的namespace之下然后用限定namespace的方式去访问它。
另外由于c中枚举被设计为常量数值的别名的本性所以枚举的成员总是可以被隐式地转换为整型。很多时候这是不安全的。为解决这一问题一般会对枚举类型进行封装。
但是封装过于复杂封装采用静态成员enum变成了非POD。传递参数的时候如果参数是结构体就不能使用寄存器来传参而整型可以通过寄存器传递。class封装版本的枚举作为函数参数传递就会带来一定性能损失。
此外枚举类型所占用的空间大小也是一个不确定的量。因为标准规定c枚举所基于的基础类型由编译器具体指定实现。
5.1.3 强类型枚举以及c11对原有枚举类型的扩展
c11引入一种新的枚举类型即枚举类又称强类型枚举。
声明强类型枚举只需在enum后面加上关键字class比如
enum class Type { General, Light, Medium, Heavy };
强类型枚举的几个优势
1.强作用域强类型枚举成员的名称不会被输出到其父作用域空间
2.转换限制强类型枚举成员的值不可以与整型隐式地互相转换。
3.可以指定底层类型。默认底层类型为int可以在枚举名称后面加上“: type”来显式指定底层类型type可以试除wchar_t之外的任何整型。
c11还对原来的枚举进行了扩展。首先底层的基本类型也可以跟强类型枚举一样显式地由程序员指定。第二则是枚举成员的名字除了会自动输出到父作用域也可以在枚举类型定义的作用域内有效。这两点都是向后兼容的。
此外在声明强类型枚举的时候也可以使用enum struct关键字两者没有任何区别。
匿名的enum class可能什么也做不了。
5.2 堆内存管理只能指针与垃圾回收
5.2.1 显式内存管理
显式内存管理常见问题
一野指针内存单元已被释放但是之前指向它的指针却还在被使用。
二重复释放。
三内存泄漏。
c11新保准对智能指针进行了改进还提供了所谓的最小垃圾回收的支持。
5.2.2 c11的智能指针
c98中智能指针通过一个模板类型“auto_ptr”来实现。auto_ptr以对象的方式管理堆分配的内存并在适当的时间比如析构释放所获得的堆内存。这种堆内存管理的方式只需要程序员将new操作返回的指针作为auto_ptr的初始值即可程序员不用再显式地调用delete。
auto_ptr存在一些缺点譬如拷贝时候返回一个左值不能调用delete[]等所在在c11标准中废弃了。c11中改用unique_ptr, shared_ptr及weak_ptr等智能指针来自动回收堆分配的对象。 每个智能指针都重载了*运算符可以用来访问所分配的堆内存。智能指针析构或者调用reset成员的时候智能指针能释放其拥有的堆内存。
unique_ptr唯一拥有所指向的对象内存仅能通过标准库的move函数来转移。unique_ptr是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。
shared_ptr允许多个智能指针共享地拥有同一堆分配对象的内存实现上采用了引用计数。shared_ptr调用reset成员函数只会引起引用计数的降低而不会导致堆内存的释放。只有在引用计数归零的时候shared_ptr才会真正释放所占有的堆内存的空间。
weak_ptr可以指向shared_ptr指针所指向的对象内存却不拥有该内存。使用weak_ptr成员lock可以返回其指向内存的一个shared_ptr对象且在所指对象内存已经无效时返回指针空值可用于验证shared_ptr的有效性。 5.2.3 垃圾回收的分类
垃圾回收的方式主要分为两大类
1.基于引用计数的垃圾回收器
优点是简单不会造成程序暂停也不会对系统缓存或者交换空间造成冲击。但是难以处理环形引用问题产生的额外开销也不小。
2.基于跟踪处理的垃圾回收器
跟踪处理的垃圾回收处理机制更为广泛地应用。其基本方法是产生跟踪对象的关系图然后进行垃圾回收。使用跟踪方式的垃圾回收算法主要有以下几种
1标记清除mark-sweep
该算法将程序中正在使用的对象视为根对象从根对象开始查找它们所引用的堆空间并在这些堆空间上做标记。当标记结束后所有被标记的对象就是可达对象reachable object或活对象live Object而没有被标记的对象就被认为是垃圾在第二步的清扫阶段就会被回收掉。
这种方法的特点是活的对象不会被移动但是其存在会出现大量的内存碎片的问题。
2标记整理mark-compact
该方法标记的方法和标记清除一样但是标记完之后不再遍历所有对象清扫垃圾了而是将活的对象向左靠齐这就解决了内存碎片的问题。
标记整理的方法有个特点就是移动活的对象因此相应地程序中所有对堆内存的引用都必须更新。
3标记拷贝mark-copy
该算法将堆空间分为两部分from和to。刚开始系统只从from的堆空间里面分配内存当from分配满的时候系统就开始垃圾回收从from对空间里找出所有活的对象拷贝到to的堆空间里。这样一来from的堆空间里面就全剩下垃圾了。而对象被拷贝到to里之后在to里是紧凑排列的。接下来是需要将from和to交换一下角色接着从新的from里面开始分配。
标记拷贝算法的一个问题是堆的利用率只有一半而且也需要移动活的对象。这种算法其实是标记整理的另一种实现而已。
5.2.4 c与垃圾回收
指针的灵活对垃圾回收带来了很大的困扰。被隐藏的指针会导致编译器在分析指针可达性时出错。c11解决这种问题的方法就是让程序员利用新的接口来提示编译器代码中存在着指针不安全的区域。
5.2.5 c11与最小垃圾回收支持
c11为了做到最小的垃圾回收支持首先对安全的指针进行了定义也就是c11中所谓的安全派生safely derived的指针。安全派生的指针是指向由new分配的对象或其子对象的指针。安全派生指针的操作包括
1.在解引用基础上的引用比如*p。
2.定义明确的指针操作比如p1。
3.定义明确的指针转换比如static_castvoid *(p)。
4.指针和整型之间的reinterpret_cast比如reinterpret_castintptr_t(p)。intptr_t是c11中一个可选择实现的类型其长度等于平台上指针的长度。
可通过get_pointer_safety函数查询编译器是否支持最小垃圾回收。
如果代码中出现了指针不安全使用的情况可以通过一些API来通知垃圾回收器不得回收该内存。即需要声明该内存为可达到的 declare_reachable()显式地通知垃圾回收器某一个对象应被认为可达到的即使它的所有指针都对回收器不可见。undeclare_reachable()则可以取消这种可达声明。
declare_reachable只需要传入一个简单的void *指针但undeclare_reachable通常被设计为一个函数模板。
有时候程序员会选择在一大片连续的堆内存上进行指针式操作为了让垃圾回收器不关心该区域也可以使用declare_no_pointers及undeclare_no_pointers函数来告诉垃圾回收器该内存区域不存在有效的指针。 5.2.6 垃圾回收的兼容性
必须限制指针的使用或者使用declare_reachable/undeclare_reachable、declare_no_pointers/undeclare_no_pointers来让一些不安全的指针使用免于垃圾回收器的检查。
此外c标准对指针的垃圾回收支持仅限于系统提供的new操作符分配的内存而malloc分配的内存则会被认为总是可达的。
更为现实的状况是本书写作时垃圾回收特性还没有得到任何表一起的支持即使是所谓的最小垃圾回收。