在我们的程序中,静态内存——用来保存局部 static 对象,类 static数据成员,以及定义在任何函数之外的变量。栈内存——用来保存定义在函数内的非 static 对象。分配在 静态内存 或 栈内存中的对象由编译器自动创建和销毁。对于栈内存,仅在其定义的程序块运行时才存在,static对象在使用之前分配,在程序结束时销毁。
除此之外,每个程序还拥有一个内存池,这部分内存被称为自由空间(堆),程序用堆来存储动态分配的对象,——即那些在程序运行时分配的对象。
1. 动态内存和智能指针
在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化;delete,接受一个动态指针,销毁该对象,并释放与之关联的内存。
新的标准提供了两种智能指针(smart pointer)类型来管理动态对象。shared_ptr允许多个指针指向同一个对象;unique_ptr则“独占”所指向的对象。标准库还定义了一个名为weak_ptr的伴随类,指向shared_ptr所管理的对象。这三种类型都定义在memory中。
1.1 share_ptr 类
类似vector智能指针也是模板。
1 shared_ptr<string> p1; //shared_ptr,可以指向string 2 shared_ptrint>> p2; //shared_ptr,可以指向int的list
智能指针的使用方法和普通指针类似,解引用返回它指向的对象,如果在一个条件判断中使用智能指针,效果就是检测它是否为空,
1 // 如果 p1 不为空,检查它是否指向一个空 string 2 if (p1 && p1->empty()) // p->mem -- (*p).mem 3 *p1 = "ds"
1.2 make_shared 函数
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。与智能指针一样,make_shared也定义在memory中。
1 //指向一个值为42的int的shared_ptr 2 shared_ptr<int> p3 = make_shared<int>(42); 3 //p4指向一个值为”999999999”的string 4 shared_ptr<string> p4 = make_shared<string>(10,'9'); 5 //p5指向一个值初始化的int,即,值为0 6 shared_ptr<int> p5 = make_shared<int>();
7 auto p6 = make_share>();
1.3 share_ptr 的拷贝和赋值
当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:
1 auto p = make_shared<int>(42); //p指向的对象只有p一个引用者 2 auto q(p); //p和q指向相同对象,此对象有两个引用者
我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数(reference count)。无论何时我们拷贝一个shared_ptr,计数器都会递增;当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计算器就会递减。
一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象:
1 auto r = make_shared<int>(42); //r指向的int只有一个引用者 2 r = q; //给r赋值,令它指向另一个地址 3 //递增q指向的对象的引用计数 4 //递减r原来指向对象的引用计数 5 //r原来指向的对象已没有引用者,会自动释放
1.4 share_ptr 自动销毁所管理的对象
当指向一个对象最后一个 share_ptr 被销毁时,share_ptr 类会自动销毁此对象。它是通过另一个特殊的成员函数——析构函数完成销毁工作的。
2. shared_ptr 和 new 结合使用
我们可以使用 new 返回的指针来初始化智能指针,智能指针是一个类,分装了一个原始的 C++ 指针,用以管理所指对象的生命期。
1 shared_ptr<double> p1; //shared_ptr可以指向一个double 2 shared_ptr<int> p2(new int (42)); //p2指向一个值为42的int
接受指针参数的智能指针构造函数是explicit的。因此,我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针:
1 shared_ptr<int> p1 = new int(1024); //错误:必须使用直接初始化形式 2 shared_ptr<int> p2(new int (1024)); //正确:使用了直接初始化
出于相同的原因,一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针:
1 shared_ptr<int> clone(int p){ 2 return new int(p); //错误:隐式转换为shared_ptr3 } 4 5 shared_ptr<int> clone(int p){ 6 7 return shared_ptr<int>(new int(p)); //正确:显式地用int*创建shared_ptr 8 }
默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象。我们可以将智能指针绑定到一个指向其他类型的资源的指针上,但是为了这样做,我们必须自己提供操作来代替 delete 。