动态内存

前言

程序中用到的不同对象有不同的生命周期。

  1. 全局对象:在程序开始时被分配,在程序结束时销毁。
  2. 局部自动对象在程序进入到定义的块体时被创建,退出时被销毁。
  3. 局部静态对象在被第一次用之前就已经创建了,当程序终止时被销毁。
  4. 分配对象的生命周期与创建的位置无关,一直存在直到显示的被释放。

程序中的内存分三种:

  1. 静态内存:用于存储局部静态对象,静态数据成员,函数体外的变量
  2. 栈内存:存储定义在函数体内的非静态对象。
  3. 堆内存:存储动态分配对象,该对象是在程序运行时分配的。

前两种内存中的对象由编译器自动创建和销毁的,最后一种的动态对象的生命周期是由程序控制,必须要显式的释放这些对象。

程序利用动态内存的原因:

  1. 不知道需要多少对象
  2. 不知道所需对象的精确类型
  3. 想在多个对象间共享数据

1 动态内存和智能指针

C++中动态内存的管理是通过:

  1. new:在动态内存中分配,选择性地初始化一个对象,返回一个指向该对象的指针。
  2. delete:接受一个指向对象的指针去销毁这个对象,释放相关的内存。

动态内存引出一个问题:在正确的时间点去释放内存,如果忘记释放内存,会造成内存泄漏。为了更好利用动态内存,有两个智能指针类型去管理动态对象。

  1. shared_ptr:允许多个指针指向相同的对象。
  2. unique_ptr:独占所指的对象。
  3. weak_ptr:是一个shared_ptr类的伙伴类,对shared_ptr管理对象的一个弱引用。

1.1 shared_ptr类

shared_ptr也是一个模板,同时使用的方式和指正类型,解引用一个智能指针就能返回指针所指的对象。

1.1.1 make_shared函数

函数在动态内存中分配和初始化一个对象,返回一个指向该对象的shared_ptr。

// shared_ptr that points to an int with value 42
shared_ptr<int> p3 = make_shared<int>(42);
// p4 points to a string with value 9999999999
shared_ptr<string> p4 = make_shared<string>(10, '9');
// p5 points to an int that is value initialized to 0
shared_ptr<int> p5 = make_shared<int>();

1.1.2 复制和赋值shared_ptrs

当我们对复制或赋值一个shared_ptr,每一个shared_ptr都记录有多少个shared_ptrs指向相同的对象:

auto p = make_shared<int>(42); // object to which p points has one user
auto q(p); // p and q point to the same object
// object to which p and q point has two users

1.1.3 自动销毁对象和释放相关内存

当shared_ptr析构会减小所指向对象的引用计数,当计数变为0的时候,shared_ptr析构器会销毁所指的对象并释放相关内存。

1.2 直接管理内存

通过newdelete

分配动态内存:
分配空闲的动态内存,初始化后并返回一个指针。

int *pi = new int; // pi points to a dynamically allocated,
// unnamed, uninitialized int

默认条件下,动态分配的对象是默认初始化的,这意味着内置或复合类型的对象具有未定义的值;类类型的对象由其默认构造函数初始化。

string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int

new无法分配所需内存时,会扔出一个bad_alloc类型的异常。

释放动态内存:
销毁指针所指的对象,释放对应的内存。

delete p; // p must point to a dynamically allocated object or be null

1.3 利用shared_ptr和new

我们能用new返回的指针初始化智能指针

shared_ptr<double> p1; // shared_ptr that can point at a double
shared_ptr<int> p2(new int(42)); // p2 points to an int with value 42

智能指针构造函数是显式的,我们不能够将一个内置指针隐式的转换为智能指针,必须直接初始化的形式去初始化一个智能指针。

shared_ptr<int> p1 = new int(1024); // error: must use direct
initialization
shared_ptr<int> p2(new int(1024)); // ok: uses direct initialization

这样利用智能指针的特性可以更好的用动态内存

1.4 unique_ptr

一次只有一个unique_ptr能够指向所给对象。当unique_ptr销毁时,所指的对象也被销毁。
当我们定义一个unique_ptr时,我们将其绑定到new返回的指针上。与shared_ptrs一样,我们必须使用直接的初始化形式:

unique_ptr<double> p1; // unique_ptr that can point at a double
unique_ptr<int> p2(new int(42)); // p2 points to int with value 42

因为unique_ptr拥有所指对象,因此不支持复制和赋值。

unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); // error: no copy for unique_ptr
unique_ptr<string> p3;
p3 = p2; // error: no assign for unique_ptr

但是有个例外:当unique_ptr将要销毁,我们可以进行复制和赋值,最常见的例子是在一个函数中返回unique_ptr:

unique_ptr<int> clone(int p) {
// ok: explicitly create a unique_ptr<int> from int*
return unique_ptr<int>(new int(p));
}

1.5 weak_ptr

该智能指针并不控制所指对象的生命周期,所指的对象是由shared_ptr管理的。将一个weak_ptr绑定到shared_ptr并不改变shared_ptr的引用计数,不管weak_ptrs是否指向对象,当最后一个shared_ptr销毁,对象也就被删除了。

auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp weakly shares with p; use count in p is
unchanged

因为对象不一定存在没所以我们不能用weak_ptr去直接访问对象,必须要调用lock函数去检查所指对象是否存在,若存在则返回一个shared_ptr。

2 动态数组

newdelete一次分配一个对象,为了能够一次分配对个对象,定义了第二种new能够分配和初始化一个对象数组,同时也有个allocator模板类将分配与初始化分离。

2.1 new和数组

int *pia = new int[5]; // pia points to the first of these ints
//利用类型别名
typedef int arrT[42]; // arrT names the type array of 42 ints
int *p = new arrT; // allocates an array of 42 ints; p points to the first
one

2.2 allocator类

将分配和构造分开,提供了一个原始的,未构造的类型内存。
allocator是一个模板:

allocator<string> alloc; // object that can allocate strings
auto const p = alloc.allocate(n); // allocate n unconstructed strings

分配的内存是没有进行构造的,所以要用construct成员利用所给的指针和参数去构造所给地址的元素。

auto q = p; // q will point to one past the last constructed element
alloc.construct(q++); // *q is the empty string
alloc.construct(q++, 10, 'c'); // *q is cccccccccc
alloc.construct(q++, "hi"); // *q is hi!

while (q != p)
	alloc.destroy(--q); // free the strings we actually allocated

当元素释放完,调用deallocate释放内存:

alloc.deallocate(p, n);//The pointer we pass to deallocate cannot be null; it must point to memory allocated by allocate.

参考

  1. C++ Primer, Fifth Edition
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值