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;
}