【C++】C++之动态内存

摘要

程序中使用的对象都有生命周期:
全局变量——程序启动时分配 结束时销毁
局部自动对象——进入定义的程序块时创建
局部static——第一次使用前分配
还有就是动态分配,其生命周期只有在显式释放的时候才销毁

为了更安全使用动态对象,标准库定义了两个智能指针来管理动态分配的对象——当一个对象应该被释放的时候,智能智能可以自动释放它。

静态内存:局部static对象、类static对象、定义在任何函数之外的变量。
栈内存:定义在函数内的非static对象
内存池(自由空间OR堆):动态分配dynamically allocate(代码控制分配和销毁)

1 动态内存与智能指针

动态内存管理

new->在动态内存中为对象分配空间并返回一个指向该对象的指针
delete->接受一个对象的指针,销毁对象,释放内存

很难正确使用,在不用了忘记释放会导致内存泄露。在指针引用内存的时候释放会非法内存。

智能指针(smart pointer)会自动释放所指向的对象。都定义在memory中

  1. shared_ptr
    允许多个指针指向一个对象。
  2. unique_ptr
    霸占一个对象。
  3. weak_ptr
    弱引用,伴随类,指向shared_ptr所管理的对象。

1.1 shared_ptr类

初始化

//默认初始化保存一个空指针。
shared_ptr<string> p1;
shared_ptr<list<int>> p2;
//解引用一个智能指针返回对象,如果条件判断智能指针,那就是检测空
if(p1 && p1->empty()){//是否为空,不空是否指向空string
	*p1="hi";
}

1.1.1make_shared分配内存

这是最安全的分配、使用动态内存的方法。调用标准库函数即可。返回值会指向T的智能指针

make_shared<T>(args)
//指向42的int的智能指针
shared_ptr<int> p3 = make_shared<int>(42);
//指向值初始化的智能指针
shared_ptr<int> p4 = make_shared<int>();//0
//最简单使用auto
auto p5 = make_shared<string>()

1.1.2 shared_ptr拷贝与赋值

拷贝或者赋值每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象:

auto q =make_shared<int>(42);//一个引用者
auto p(q);//该对象两个引用者

当share_ptr赋值或者销毁(离开作用域)时计数器就会减1

auto r =make_shared<int>(42);//一个引用者
auto  r = q ;//递增q,递减r。r没有引用的会自动释放

事实上记录多少个指针共享对象,由标准库具体的实现决定。可能时计数器可能是其他数据结构。

1.1.3 shared_ptr自动销毁管理的对象

当对象的最后一个shared_ptr被销毁的时候,shared_ptr类会自动销毁此对象。通过——析构函数完成工作(控制此类型的对象销毁时做什么操作。一般用来释放对象所分配的资源 (内存))

shared_ptr的析构函数会递减它所指向的对象的引用次数。如果次数为0,那么就会销毁对象,并释放内存。

在函数里面虽然是局部变量,但是你如果return了shared_ptr,相当于引用他。计数器+1,就不会销毁

1.1.4 例子strblob

不能在bolb的对象内直接存vector因为离开作用域的时候对象销毁导致成员也会被销毁。为了保证一个类被销毁的时候其元素继续存在,使用动态内存。类的元素类型为shared_ptr。

1.2直接管理new delete

new分配内存,delete释放new分配的内存。

1.2.1new动态分配与初始化

new返回一个指向该对象的指针,因为对象没有命名。

int *pi = new int//没有初始化

动态分配的对象是默认初始化的,如果不支持初始化(内置类型、组合类型的对象)值是未定义的。

string *p =new string;//空string
int *p =new int;//未初始化的int

直接初始化来初始化动态分配的对象,传统——圆括号,列表——花括号

int *pi=new int(1);
vector<int> * q = new vector<int>{1,2,3,4,5,6,7,8} 
int * qq = new  int();//空int

auto p = new auto(qq);//使用qq对p初始化

1.2.2内存耗尽

如果内存耗尽,new会抛出bad_alloc的异常

int *p1 =new  int;//失败会std::bad_alloc
int *pi =new (nothrow) int;//失败返回nullptr-》定位new

1.2.3释放动态内存delete

delete的对象必须是一个动态分配的对象或者是一个空指针。

delete p

与shared_ptr不同的是,new在显式释放之前它都是存在的。
比如说在函数中new了一个动态内存,跳出函数后,那块地址就没办法销毁了。
常见问题:

  1. 忘记delete内存
  2. 使用已经释放的对象
  3. 同一块内存释放两次
    解决:坚持使用智能指针,并且在delete之后把指针置为nullptr,避免空悬指针(指向的对象没了)

但是对于多个指针指向相同的内存,我们需要一个个去寻找空指针,这是比较困难的。

1.3shared_ptr智能指针和new动态内存结合

我们可以使用new的返回值来初始化智能指针
(!!!必须直接初始化,因为不能将一个内置指针隐式的转换为智能指针)

shared_ptr<int> p (new int(42));//指向值为42的int
shared_ptr<int> p =new int(42);//是错误的

默认初始化智能指针的普通指针必须指向动态内存,否则需要自己释放

shared_ptr<T> p(q,d)//p接管q指向的对象的所有权,q必须可以转换为T *。p会调用d来实现delete

1.3.1 不要使用get初始化或为智能指针赋值

get()可以返回内置指针,指向智能指针管理的对象,为了给不支持智能指针的对象传递。

1.3.2智能指针和哑类

有些类没有制定析构函数,需要显式的释放所使用的任何资源。默认的时候delete释放动态内存。

如果使用智能指针管理的资源不是new分配的时候,需要传递一个删除器释放。来替代delete

1.4 unique_ptr

unique_ptr拥有它指向的对象。某个时刻只能有一个unique_ptr指向给定的对象。当unique_ptr销毁的时候对象也被销毁。

定义的时候需要将其绑定到一个new返回的指针上。——》使用直接初始化。不支持拷贝和赋值

unique_ptr <int> p(new int(432));

但是有一些操作是支持的、

unique_ptr<string> p2(p1.release());//p1置空,返回p1指向的对象
unique_ptr<string> p3(string("abcc"));
p3.reset(p2.release());//reset使得p3重新指向给定的指针。

在函数里面,可以返回unique_ptr的拷贝。

1.5weak_ptr

不控制生命周期的智能指针,它指向一个shared_ptr管理的对象。绑定到shared_ptr的时候不会改变引用的计数.

auto p = make_shared<int>(42);
weak_ptr<int> wp(p);//wp共享p,p引用次数不变。

因为对象可能被释放,所以我们调用lock检查对象是否存在

if (shared_ptr<int> np ==wp.lock()){if中np wp共享对象
}

wp.lock()返回shared_ptr指针。

2动态数组

C++定义一种new表达式语法。可以分配并初始化一个对象数组,allocator类允许我们将分配和初始化分离。

2.1new和数组

new分配一个对象数组

int *pia - new int[a];//pia指向第一个int 

a必须是整形,不必是常量。
也可以使用数组类型的类型别名

typedef int aarT[42];
int *p = new arrT;//p指向第一个int

2.1.1分配一个数组得到元素类型的指针(动态数组)

因为不是数组类型的指针,所以没办法调用begin end。

2.1.2释放动态数组

delete [] p;

2.1.3智能指针和动态数组

unique_ptr<int[]> up(new int[10]);
up.release();//自动使用delete[]销毁指针

2.2 allocator类

new:
内存分配
对象构造

delete
析构
内存释放
缺点不能分离。
我们希望在一大块内存的时候能把内存分配和对象构造分离。

2.2.1 allocator类

在memory中,将内存分配和对象构造分开。

使用类型感知的内存分配方法,分配的内存是原始的、未构造的。与vector类似,aocator是模板

当分配内存时,会自动根据给定的类型确定恰当的内存和对齐位置。

allocator<string> alloc;
auto const p =alloc.allocate(n);//分配n个未初始化的string
auto q =p;
alloc.construct(q++,10,'c')//*q="cccccccccc"

使用allocator返回的内存,就必须用construct构造对象,使用完对象后使用destory销毁

while(q!=p)
	alloc.destory(--1);
alloc.deallocate(p,n);//释放内存

2.2.3拷贝填充

ubibitialized_copy()
ubibitialized_fill()

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页