动态内存与智能指针

来源:互联网 发布:被淘宝托管骗了怎么办 编辑:程序博客网 时间:2024/05/18 13:28

1. 内存简单分类

静态内存:保存局部static对象、类static数据成员、在任何函数之外定义的变量;由编译器自动创建与销毁。

栈内存: 保存定义在函数内的非static对象;仅在定义的程序块运行时才存在。

堆内存: 保存那那些程序运行时分配的动态对象,动态对象的生存期由程序来控制;由程序代码显式地销毁。

2. share_ptr指针

初始化

调用make_shared函数初始化:make_shared(args)
e.g :

shared_ptr<int> p = make_shared<int>(); auto p1 = make_shared<int>();

使用new操作符初始化:shared_ptr<int> p2 (new int(42));
注:必须显示初始化智能指针;

拷贝与赋值

e.g:

p = p1;auto p(p1);

每个shared_ptr有一个关联的计数器(引用计数);

拷贝时,计数器递增,常见的拷贝情形有:
(1) 一个shared_ptr初始化另外一个shared_ptr时;
(2) 将shared_ptr传递给一个函数;
(3) 作为函数的返回值;

当一个shared_ptr对象的计数器值为0时,自动释放自己所管理的对象;

计数器递减的常见情形:
(1)给一个shared_ptr赋予一个新值;
(2)shared_ptr被销毁时,例如离开其作用域;

e.g

auto r = make_shared<int>(42);r = q;  //给r赋值,令它指向另外一个地址;        //递增q的引用计数        //递减r原来的所指的对象的引用计数        //r原来窒息那个的对象已没有引用,则会自动释放

改变shared_ptr的操作

3.直接管理内存

c++语言定义了两个运算符来分配和释放内存,运算符new分配内存,delete负责释放分配的内存。

若使用直接管理内存的类,不能依赖类对象拷贝、复制、销毁操作的编译器所设置任何默认定义。

3.1 new分配动态内存

初始化

(1)默认初始化:默认情况下为默认初始化,内置类型或组合类型的对象值将是未定义的,类类型则使用默认构造函数进行初始化;

(2)直接初始化:传统初始化(圆括号),列表初始化(花括号);

(3)值初始化:类型名后面加一对空括号;

(4)使用auto推断:提供一个括号包围的初始化器,只接受单一初始化器;

e.g:

int *pi = new int;      //默认初始化int *ps = new string(10,'9');   //圆括号直接初始化vector <int> *pv = new vector<int> {1,2,3,4};//花括号列表初始化int *pi1 = new int();       //值初始化auto p1 = new auto (obj)    //使用auto 初始化器

3.2 delete释放动态内存

动态对象生存期为知道被释放为止,必须通过delete释放动态内存。

使用delete释放动态内存时,编译器不能分辨一个指正指向的是静态还是动态分配的对象。

3.2.1 直接管理内存容易引起的常见问题:

(1) 忘记delete内存,导致内存泄露;
(2) 使用已经释放掉的对象;释放内存后将指针置为空,或许可以避免;
(3) 同一块内存释放两次。

3.2.2 使用普通指针与智能注意

混合使用普通指针和智能指针很危险!

考虑下面的代码:

void process (shared_ptr<int> ptr){    //使用ptr}//ptr离开作用域,被销毁

若正常传递一个shared_ptr指针:
(1)调用该函数时,process的参数是指传递,实参会被拷贝给ptr
(2)递增其引用数,因此引用计数值至少为2,当process结束后局部变量ptr销毁,但ptr指向的内存不会被释放。

若传递一个普通指针如下:

int main(){    int *iptr = new int(42);    process(shared_ptr<int>(iptr));    *iptr = 233;//error}

**注:**iptr拷贝给一个临时的shared_ptr,该智能指针计数器为1,当结束process函数时,临时的智能指针销毁,释放内存,因为与iptr指向的内存相同;iptr也会失效,使用iptr指针将会产生错误。

正确做法:

share_ptr<int> iptr(new int 42);//计数值:1process(iptr);  //拷贝,计数值递增:2                //离开函数,计数值:1*iptr = 233;    //安全
不要使用get初始化另外一个智能指针或赋值

3. unique_ptr指针

初始化

unique_ptr 没有类似make_shared的标准库函数返回指针,故:

unique_ptr<int> p2(new int(42));//直接初始化

拷贝与赋值

unique_ptr不支持普通的拷贝或赋值;

但通过release或reset指针可以从一个(非const)unique_ptr转移给另外一个unique:

unique<int> p1 (new int (42));unique<int> p2 (p2.release());//release将所有权转移给p2,并将p1置空unique_ptr<int> p3 (new int(233));p2.reset(p3.release());//reset释放了原来p2的指向的内存

不能拷贝的一个例外:可以拷贝一个将要销毁的unique_ptr值。
e.g: 从函数返回一个unique_ptr

unique_ptr<int> clone(int p){    return unique_ptr<int> (new int(p));}

传递删除器

e.g unique_ptr<T,d> u(d)

4. weak_ptr指针

weak_ptr指针一种不控制所指对象生存期的智能指针,指向一个shared_ptr管理的对象,即绑定给一个shared_ptr不会改变shared_ptr的引用计数。

0 0
原创粉丝点击