C++(2)----智能指针与动态内存
C++ 11提供的智能指针有:shared_ptr、unique_ptr、weak_ptr。在 头文件 memory 中。
一、new delete 直接管理内存
1、初始化
string * ps = new string // 初始换为一个空string int * pi = new int ;//pi 指向一个未初始化的int,*pi 未定义 int * pi = new int(1024); // pi 指向的对象值为1024 string *ps = new string(10,'9') ; // *ps 为"9999999999" vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};
2、释放delete
int i, *pi1 = &i, *pi2 = nullptr; double *pd = new double(33), *pd2 =pd; delete i; // 错误,, i不是一个动态分配的指针 delete pi1; // 未定义错误, pi1指向一个栈内存指针 delete pd; //正确 delete pd2; //未定义错误, pd2指向的内存已经被释放 delete pi2; //正确:不能释放一个空指针
delete 之后应该重置指针,
int *p(new int(42)); auto q =p; delete p; p=q=nullptr
3、使用new delete 管理内存常出现的问题
没有delete内存,造成内存泄漏
使用已经释放掉的对象
同一块内存释放两次
二、shared_ptr:
允许多个指针指向同一个对象。
定义与初始代码如下:
// 定义shared_ptr ,默认初始化为空指针 shared_ptr<string> p1; shared_ptr<list<int>> p2; // make_shared 函数, 头文件 memory // 返回一个shared_ptr, // make_shared<T>(args) 使用args初始化对象 shared_ptr<int> p3 = make_shared<int>(42); shared_ptr<string> p4 = make_shared<string>(10,'9') // "9999999999" shared_ptr<int> p5 = make_shared<int>(); //初始化为 0
和new 结合初始换
shared_ptr<int>p2(new int(42)); // 不能混淆普通指针和只能指针 shared_ptr<int> p1 = new int(1000); // 错误 shared_ptr<int> p2(new int(1024)); // 正确 // 错误 shared_ptr<int> clone(int p) { return new int(p); } // 正确 shared_ptr<int> clone(int p) { return shared_ptr<int>(new int(p)); }
shared_ptr的拷贝和赋值、自动释放
引用计数; 当对象不再使用时,shared_ptr 会自动释放动态对象;
auto p = make_shared<int>(42); //p指向的对象只有p一个引用者 auto q(p); // 递增一个引用计数 auto r = make_shared<int>(42); //引用计数为1 r=q ; // 给r赋值; r原来指向的对象引用计数减1; q指向的对象引用计数+1; r原来指向的对象没有引用,自动释放
当引用计数为0是,内存会被释放,当普通指针 和智能指针混用是可能会出现错误。
// 一个函数,参数为 shared_ptr<int> void process(shared_ptr<int> ptr) {A // 使用ptr }// ptr离开作用于,销毁释放 /******** 正确 ********/ shared_ptr<int> p(new int(42)); // 引用计数为1 process(p); // 参数作为值传递,在函数内部引用计数为2,之后为1 int i = *p; //内有问题, 引用计数值为1; /********* 错误 ********/ int *x(new int(1024)); // 这是一个普通的指针 process(x) ; // 错误,普通指针不能转换为 shared_ptr<int> porcess(shared_ptr<int>(x)); //合法,但是危险,函数结束后,会释放内存 int j =*x ; // 错误,此时内存已经被释放
谨慎的使用get() 函数,只有保证内存不被释放的前提现使用。
不能用get 初始化另一个智能指针,或为另一个智能指针赋值
// 谨慎使用 get() 函数 shared_ptr<int> p(new int(42)); // 引用计数为1 int * q = p.get(); // 合法,但是保证内存释放的时机 {// 程序块 // 不要使用get() 对另一个智能指针初始化,这样对同一块内存,有多个计数 shared_ptr<int>(q); }// q被销毁释放 int foo = *p; // 此时p所指向的内存已经被释放
在发生异常时,智能指针也能保证内存被释放
// 使用 new 分配内存,应当注意由于分支 和 异常造成的内存泄漏 void f() { int *ip = new int(42); // new 动态分配 // 发生异常 delete ip; // 之前发生异常,这里可能造成内存泄漏 } // 使用智能指针, 在发生异常时,内存也会被释放 void f() { shared_ptr<int> sp (new int(42)); // 分配一个新对象 // 发生异常 }// 在函数结束时,shared_ptr 被销毁,同时释放指向的内存
资源管理《c++primer》:
三、unique_ptr:
独占,同一时间只有一个智能指针可以指向这个对象。
不支持普通的拷贝,和赋值
unique_ptr<string> p3 (new string ("auto")); //#4 unique_ptr<string> p4; //#5 p4 = p3; // 出错
unique_ptr<string> p4(p3); //出错:不支持拷贝
unique_ptr<string> p2(p1.release()) // release 将p1置为空
unique_ptr<string> p3(new string("Trex"));
// 将所有权从p3转移给p2
p2.reset(p3.release());
四、weak_ptr
不控制所指向对象生存期。
指向由一个shared_ptr管理的对象(由shared_ptr管理生命周期)
将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数
// 初始化 auto p = make_shared<int>(42); weak_ptr<int> wp(p); // 若共享,p的技术不变 // 生命周期由shared_ptr 管理,因此weak_ptr指向的对象可能为空 // 可使用lock 检查是否为空 // lock 返回一个指向共享对象的shared_ptr if(shared_ptr<int> np = wp.lock()) { ....... }
五、智能指针与动态数组
int *pia = new int[10]; // 10个未初始化的int int *pia2 = new int[10](); //10个值初始化为0的int string *psa = new string[10]; //10个空string string *psa2 = new string[10](); // 10个空string int * pia3 = new int[3]{0,1,2}; delete [] pia;
unique_ptr<int[]> up(new int[10]);
up.release(); //自动使用delete[] 销毁指针
// allocator 在头文件memory 中 // 将内存分配和对象构造分离 // 分配原始 未构造 // 是一个模板 allocator<string> alloc; //可以分配string的allocator 对象 auto const p = alloc.allocate(n); //分配n个未初始化的string
参考:
https://blog.csdn.net/k346k346/article/details/81478223
https://www.cnblogs.com/WindSun/p/11444429.html
《c++ primer》第12章