【c++ primer读书笔记】【第12章】动态内存

1、在c++中,动态内存管理通过一对运算符完成:new,在动态内存中为对象分配空间并返回一个指向该对象的指针。delete,接受一个动态对象的指针,销毁该对象,并释放与之相关的内存。

2c++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;
}

原文地址:https://www.cnblogs.com/ruan875417/p/4495570.html