1. 强类型枚举
//c++11的强类型枚举可以指定底层基于的基本类型,我们可以避免编译器不同而带来的不可移植性。
//另外,设置较小的基本类型也可以节省内存空间。
enum class C : char {C1 = 1, C2 = 2};
enum class D : unsigned int {D1 = 1, D2 = 2, Dbig = 0xFFFFFFF0U};
cout << "sizeof(C):" << sizeof(C) << endl; //1
cout << "sizeof(C::C1):" << sizeof(C::C1) << endl; //1
cout << "sizeof(D::D1)" << sizeof(D::D1) << endl; //4
2. 堆内存管理:智能指针与垃圾回收
2.1 显式内存管理
从语言层面来讲,c++中内存相关的问题可以归纳为三个方面。
- 野指针:一些内存单元已被释放,之前指向它的指针却还在被使用。这些内存有可能被运行时系统重新分配给程序使用,从而导致了无法预测的错误。
- 重复释放:程序试图去释放已经被释放过的内存单元,或者释放已经被重新分配过的内存单元,就会导致重复释放错误。通常重复释放内存会导致 c/c++ 运行时系统打印出大量错误及诊断信息。
- 内存泄漏:不再需要使用的内存单元如果没有被释放就会导致内存泄漏。如果程序不断重复进行这类操作,将会导致内存占用剧增。
2.2. C++11 智能指针
void testUniquePtr()
{
cout << "enter testUniquePtr()...................................................................." << endl;
unique_ptr<int> up1(new int(1));
//unique_ptr<int> up2 = up1; //编译失败,unique_ptr所指对象的内存的所有权只能通过标准库的 move 函数来转移
unique_ptr<int> up2 = move(up1); //up2是数据唯一的 unique_ptr 智能指针
cout << "*up2:" << *up2 << endl;
unique_ptr<int> up3(move(up2));
cout << "*up3:" << *up3 << endl;
up1.reset();
up2.reset();
up3.reset();
cout << "return from testUniquePtr()...................................................................." << endl;
}
从实现上讲, unique_ptr 是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。程序员仅可以使用右值对 unique_ptr 对象进行构造,而且一旦构造成功,右值对象中的指针即被“窃取”,因此该右值对象即刻失去了对指针的“所有权”。
shared_ptr<int> sp1(new int(22));
shared_ptr<int> sp2 = sp1;
auto sp3 = sp1;
cout << "*sp1:" << *sp1 << endl; //22
cout << "*sp2:" << *sp2 << endl; //22
sp1.reset();
cout << "*sp2:" << *sp2 << endl; //22
cout << "*sp3:" << *sp3 << endl; //22
sp2.reset();
cout << "*sp3:" << *sp3 << endl; //22
而 shared_ptr 同样形如其名,允许多个该智能指针共享地“拥有”同一堆分配对象的内存。与 unique_ptr 不同的是,由于在事项上采用了引用计数,所以一旦一个 shard_ptr 指针放弃了“所有权”,其他的 shared_ptr 对对象内存的引用并不会受影响。某个智能指针调用 reset 知会导致引用计数的降低,不会导致堆内存的释放。
2.3 垃圾回收的分类
我们把之前使用过,现在不再使用或者没有任何指针再指向的内存空间就称为 “垃圾”。而将这些 “垃圾”收集起来以便再次利用的机制,就被称为“垃圾回收”。
在当前流行的编程语言中,除过 c 和 Pascal 不支持,c++ 部分支持外,其他语言大多都支持自动回收。
垃圾回收的方式虽然很多,但主要可以分为两大类:
(1)基于引用计数的垃圾回收器
简单地说,引用计数主要是使用系统记录对象被引用(引用、指针)的次数。当对象被引用的次数变为 0 时,该对象即被视作 “垃圾”而回收。使用引用计数做垃圾回收的算法的一个优点是实现很简单,与其他垃圾回收算法相比,该方法不会造成程序暂停,因为技术的增减与对象的使用是紧密结合的。此外,引用计数也不会对系统的缓存或者交换空间赞成冲击,因此被认为“副作用”较小。但是这种方法比较难处理“环形引用”问题,此外由于技术带来的额外开销也并不小,所以在使用上也有一定的限制。
(2)基于跟踪处理的垃圾回收器
相比于引用计数,跟踪处理的垃圾回收机制被更广泛地引用。器基本方法是产生跟踪对象的关系图,然后进行垃圾回收。使用跟踪方式的垃圾回收算法主要有以下几种:
- 标记-清除
- 标记-整理
- 标记-拷贝
2.4 c++ 与垃圾回收
指针的灵活性使用可能是 c/c++ 中的一大优势,而对于垃圾回收来说,却会带来很大的困扰。