1、在c++中,动态内存管理通过一对运算符完成:new,在动态内存中为对象分配空间并返回一个指向该对象的指针。delete,接受一个动态对象的指针,销毁该对象,并释放与之相关的内存。
2、c++11新标准库提供了两种智能指针类型管理动态对象,智能指针的行为类似常规指针,区别是它自动释放所指向的内存。shared_ptr允许多个指针指向同一个对象,unique_ptr独占所指向的对象,伴随类weak_ptr指向share_ptr所管理的对象。
3、最安全的使用动态内存的方法是使用一个make_shared的函数。此函数在动态内存中分配一个对象并初始化,返回指向此对象的shared_ptr。
shared_ptr<int> p1 = make_shared<int>(42); //p1是指向一个值为41的int的shared_ptr shared_ptr<string> p2 = make_shared<string>(5, '9');//p2指向一个值为“99999”的string shared_ptr<int> p3 = make_shared<int>(); //p3指向一个值初始化的int
4、shared_ptr的拷贝和赋值
shared_ptr<int> p = make_shared<int>(42); //p是指向一个值为42的int的shared_ptr auto q(p);//p和q指向相同对象,此对象有两个引用者 auto r = make_shared<int>(42); r = q; //给r赋值,令它指向另一个地址,递增q指向的对象的引用计数,递减r原来指向的对象的引用计数 //r原来指向的对象已没有引用者,会自动释放我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论我们拷贝一个share_ptr,计数器都会递增。当我们给一个shared_ptr赋值或者shared被销毁,计数器就会递减。
#include<iostream> #include<memory> using namespace std; int main(){ shared_ptr<int> p = make_shared<int>(42); //p是指向一个值为41的int的shared_ptr cout << "p use_count:" << p.use_count() << endl; //返回与p共享对象的智能指针数量 cout << "p unique:" << p.unique() << endl; //若p.use_count()为1,返回true,否则返回false auto q(p);//p和q指向相同对象,此对象有两个引用者 cout << "q use_count:" << q.use_count() << endl;//返回与q共享对象的智能指针数量 cout << "q unique:" << q.unique() << endl;//若q.use_count()为1,返回true,否则返回false auto r = make_shared<int>(42); cout << "r use_count:" << r.use_count() << endl; r = q; //给r赋值,令它指向另一个地址,递增q指向的对象的引用计数,递减r原来指向的对象的引用计数 //r原来指向的对象已没有引用者,会自动释放 cout << "r use_count:" << r.use_count() << endl; //与r共享对象的智能指针数量为3,分别是p,q,r cout << "q use_count:" << q.use_count() << endl;//与q共享对象的智能指针数量为3,分别是p,q,r system("pause"); return 0; }
5、当指向对象的最后一个shared_ptr被销毁时,shared_ptr 类会自动销毁此对象。它通过析构函数完成销毁工作。shared_ptr 的析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的函数就会销毁对象,并释放它占用的资源。
6、一个unique_ptr 拥有它所指向的对象,和shared_ptr不同,某个时刻只能有一个unique_ptr 指向一个给定对象,当unique_ptr 被销毁时,对象也被销毁。
#include<iostream> #include<memory> #include<string> using namespace std; int main(){ unique_ptr<string> p1(new string("aaa")); //unique_ptr<string> p2(p1); //错误,unique_ptr不支持拷贝 unique_ptr<string> p3; //p3 = p1; //错误,unique_ptr不支持赋值 unique_ptr<string> p4(p1.release()); //p1.release()将p1置为空,将所有权从p1转移给p4 unique_ptr<string> p5(new string("bbb")); p5.reset(p4.release()); //reset释放了p5原来指向的内存,p5指向了p4原来指向的内存 cout << *p5 << endl; system("pause"); return 0; }
7、类似shared_ptr,unique_ptr默认情况下用delete释放它指向的对象。和shared_ptr一样,我们可以重载一个unique_ptr中默认的删除器类型。重载一个unique_ptr中的删除器会影响到unique_ptr类型及如何构造该类型的对象。
格式://p指向一个类型为objT的对象,并使用一个类型为delT的对象释放objT对象 //它调用一个名为fcn的delT类型对象 unique_ptr<objT, delT> p(new objT, fcn);
8、weak_ptr 是一种不控制对象生存期的智能指针,它指向由一个shared_ptr 管理的对象。将weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,当最后一个指向对象的shared_ptr被销毁,即使有weak_ptr指向该对象,对象还是会被释放。
9、allocator类
allocator类将内存分配和对象构造分离开来,它分配的内存是原始的,未构造的。
#include<iostream> #include<memory> #include<string> using namespace std; int main(){ allocator<string> alloc; //定义了一个名为alloc的allocator对象,它为类型为string的对象分配内存 auto const p=alloc.allocate(10); //分配内存保存10个类型为string的对象 auto q = p; //q指向最后构造的元素之后的位置 alloc.construct(q++, 5, 'c');//*q为ccccc,q指向的内存中构造一个对象 alloc.construct(q++); //*q为空字符串 alloc.construct(q++, "hi"); //*q为hi cout << *p << endl; //正确,输出ccccc //cout << *q << endl; //错误,q指向未构造的内存 while (q != p) alloc.destroy(--q); //销毁真正构造的string alloc.deallocate(p, 10);//释放p中地址的内存,这快内存保存了n个string对象,p必须是allocte返回的指针,n必须是allocate(n)的n //在调用deallocate时必须先毁坏这块内存中创建的对象 system("pause"); return 0; }
10、allocator类的拷贝和填充未初始化内存的算法
#define _SCL_SECURE_NO_WARNINGS //为了防止VS2013报错 #include<iostream> #include<memory> #include<vector> using namespace std; int main(){ vector<int> vec{ 1,2,3 }; vector<int> vec2{ 5,6,7,8}; allocator<int> alloc; auto p1=alloc.allocate(vec.size()*4); auto p2 = uninitialized_copy(vec.begin(), vec.end(), p1); //uninitialized_copy(b,e,b2); //从迭代器b和e指定的输出范围中拷贝元素到迭代器b2指定的未构造的原始内存中,b2指向内存必须足够大 auto p3=uninitialized_copy_n(vec2.begin(), vec2.size(), p2); //uninitialized_copy_n(b, n, b2) 从迭代器b指向的元素开始拷贝n个元素到到以b2开始的内存中 auto p4 = uninitialized_fill_n(p3, vec.size(), 10); //uninitialized_fill_n(b, n, t) 从b指向的内存地址创建n个对象,t是填充的元素 uninitialized_fill(p4, p4 + 2, 9); //uninitialized_fill(b, e, t) 在b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝 for (size_t i = 0; i < vec.size() * 4; ++i) cout << *p1++ << " "; cout << endl; system("pause"); return 0; }