- 静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量。由编译器自动创建和销毁。
- 栈内存用来保存定义在函数内的非static对象。对于程序块运行时才存在;static在使用之前分配,程序结束时销毁。
- 除了静态内存和栈内存,每个程序还有一个内存池。称作heap(堆内存)。程序用堆来存储动态分配的对象–即哪些在程序运行时分配的对象。这些动态对象的生存周期由程序来控制,所以我们代码必须显示地销毁它们。
- 动态内存的管理是通过一对运算符来完成:new,delete。new在动态内存中为对象分配空间,并返回一个指向该对象的指针。delete接受一个动态对象的指针,销毁该对象并释放与之关联的内存。
新的标准库提供了两种智能指针类型来管理动态对象。
它可以负责自动释放所指向的对象。定义在memory头文件中
shared_ptr 允许多个指针指向同一对象;
unique_ptr “独占”所指向的对象
weeak_ptr 这是一个弱引用,指向shared_ptr所管理的对象
- shared_ptr类的操作
对于shared_ptr指针的创建,需要指出所指向对象的类型
//默认初始化的智能指针中保存着一个空指针
shared_ptr<string> p1; //shared_ptr,可以指向string
shared_ptr<list<int>> p2; //shared_ptr,可以指向int的list
//检查p1是否为空指针,检查p1指向的string对象是否为空
if(p1 && p1->empty())
*p1 = "hi"; //p1不为空,指向一个空string的话,解引用p1,赋值为string
//适用于shared_ptr和unique_ptr的操作
shared_ptr<T> sp
unique_ptr<T> up 空智能指针,可以指向类型为T的对象
p 可以作为一个判断条件,获得它所指向的对象
*p 解引用p,获得它指向的对象
p->men 等价于(*p).mem
p.get() 返回p中保存的指针。若智能指针释放了其对象,返回的指针所指向的对象也就消失
swap(p, q) 交换p和q中的指针
p.swap(q)
适用于shared_ptr的独有操作
make_shared<T> (args) 函数返回一个shared_ptr指针,指向一个T类型的动态对象,使用args初始化对象
shared_ptr<T> q(p) q是shared_ptr(p)的拷贝;此操作会递增p中的计数器。
p = q p和q都是shared_ptr类型,所保存的指针必须能相互转换。
此操作会减少一次p的引用次数,增加一个q的引用次数。若p的次数减为0,会释放其管理的内存
p.unique() 若p.use_count()为1,返回true;否则返回false
p.use_count() 返回与p共享对象的智能指针数量;可能很慢,用于调试。
最安全的分配和使用动态内存的方法是调用make_shared函数
//指向一个值为42的int的shared_ptr
shared_ptr<int> p = make_shared<int>(42);
//指向一个“99999”的字符串
shared_ptr<string> ps = make_shared<string>(s, '9');
//指向一个值初始化的int,即值为0
shared_ptr<int> pi = make_shared<int>();
auto p = make_shared<int>(42); //此时p指向的对象只有p一个引用
auto q(p); //p和q指向相同对象,此对象有两个引用者
auto r = make_shared<int>(42); //r指向的int只有一个引用者
r = q; //此操作给r赋值,使其指向另一地址
//r原来指向的对象的引用数减一,若减为零,则原来指向的对象会被释放
shared_ptr销毁所管理的对象时,调用的时析构函数完成
- 程序出于以下三种原因使用动态内存:1. 程序不知道自己需要使用多少个对象 2. 程序不知道所需要对象的准确类型。3. 程序需要在多个对象间共享数据。
容器类时出于第一种原因而使用动态内存的例子
例如每个vector拥有自己的元素,当我们拷贝一个vector时,原vector和副本vector中的元素时相互分离
vector<int> v1;
{
vector<int> v2 = { 1, 2, 3 };//v2属于块作用域
v1 = v2;
}
//v2不再起作用,v2及其包含的元素也被销毁
但某些类分配的资源具有与原对象相独立的生存期。
一般而言,如果两个对象共享底层的数据,当某个对象被销毁时,不能单方面销毁底层数据
假设存在Blob类
Blob<string> b1; //空Blob
{
Bolb<string> b2 = {"a", "bs", "scs"};
b1 = b2; //b2和b1共享元素
}
//b2被销毁了,但是共享的元素不能被销毁,b1指向b2创建的元素
- 定义StrBlob类示例
尝试定义一个管理string的类,命名为StrBlob,本例子将使用vector来保存元素
思路:
我们不能在Blob对象内直接保存vector,因为一个对象的成员在对象销毁时也会被销毁
例如,有两个Blob类b1,b2共享相同的vector,加入vector保存在b2中,当b2销毁时,vector也会被销毁
为了保证b2销毁时,vector仍然存在,我们将其保存在动态内存中
为了实现所希望的数据共享,为每个StrBlob设置一个shared_ptr来管理动态分配的vector。
此shared_ptr的成员将记录有多少个StrBlob共享相同的vector,并在vector的最后一个使用者销毁时释放vector。
class StrBlob
{
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob();
StrBlob(std::initializer_list<std::string> il);
size_type size() const { return data->size(); } //指针用箭头,实体用点
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const std::string &t) { data->push_back(t); }
void pop_back();
//元素访问
std::string& front();
std::string& back();
private:
std::shared_ptr<std::vector<std::string>> data;
void check(size_type i, const std::string &msg) const;
};
//两个构造函数都使用列表初始化data成员
StrBlob::StrBlob() : data(make_shared<vetor<string>>()) {}
StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {}
//此类中我们实现了size,empty和push_back成员。这些成员通过指向底层vector的data成员来完成。
//例如对一个StrBlob对象调用size,会调用data->size()。
//当有些操作访问元素时,必须检查元素是否存在。因此定义了一个check工具函数,检查索引是否合法
void StrBlob::check(size_type i, const std::string &msg) const
{
if(i>=data->size())
throw out_of_range(msg);
}
//pop_back和元素访问成员函数首先调用check。
//如果成功,则继续利用底层的vector的操作来完成工作
string& StrBlob::front()
{
check(0, "front on empty StrBlob!");
return data->front()
}
string& StrBolb::back()
{
check(0, "back on empty StrBolb!");
return data->back();
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBolb!");
return data->pop_back();
}
//front和back应该对const进行重载