c++ primer(第五版)笔记 第十二章 动态内存(1)

31 篇文章 0 订阅
// 静态内存(static) 局部static, 类static, 定义在任何函数之外的变量,由编译器自动创建和销毁,程序使用前创建,程序结束时销毁,
// 栈(stack)		函数内的非static 对象,由编译器自动创建和销毁,仅在其定义的程序块运行时存在
// 堆(heap)			动态分配的对象,运行时分配的对象,程序显式的销毁

// 动态内存的管理
	// new		在动态内存中为对象分配空间并返回指向该对象的指针,可选择进行初始化
	// delete	接受动态对象的指针,销毁该对象,释放与之关联的空间

#include<memory>	
#include<iostream>	
#include<string>	
#include<initializer_list>	
#include<vector>	
#include<stdexcept>	
#include<new>	

using std::shared_ptr;
using std::unique_ptr;
using std::make_shared;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::initializer_list;
using std::vector;
using std::out_of_range;
using std::nothrow;

// 对象间共享数据类定义
class StrBlob{
public:
	typedef vector< string>::size_type size_type;
	// 构造函数
	StrBlob();
	StrBlob( initializer_list< string> il);
	// 普通成员函数
	size_type size() const {
		return data->size();
	}
	size_type count() const {
		return data.use_count();
	}
	bool empty() const {
		return data->empty();
	}
	// 添加删除
	void push_back( const string &s){
		data->push_back( s);
	}
	void pop_back();
	// 访问
	string& front() const;
	string& back() const;
	
private:
	// 数据
	shared_ptr< vector< string>> data;
	// 检查 data[i] 的合法,抛出异常	
	void check( size_type i, const string &s) const;
};

// 构造函数
StrBlob::StrBlob() : data( make_shared< vector< string>>()){}
StrBlob::StrBlob( initializer_list< string> il) : 
				data( make_shared< vector< string>>( il)){}
				
// 检查 data[i] 的合法,抛出异常		
void StrBlob::check( size_type i, const string &s) const{
	if( i >= data->size())
		throw out_of_range( s);
}
// 删除
void StrBlob::pop_back(){
	check(0, "pop_back on empty strBlob");
	data->pop_back();
}
// 访问
string& StrBlob::front() const {
	check(0, "front on empty strBlob");
	data->front();
}
string& StrBlob::back() const {
	check(0, "back on empty strBlob");
	data->back();
}
// 返回动态内存
vector< int> *get_pvi(){
	return new vector< int>();
}

void save_pvi(vector< int> * pvi){
	int n = 0;
	while( cin >> n)
		pvi->push_back( n);
}
void print_pvi(vector< int> * pvi){
	for( auto &w : *pvi)
		cout << w << endl;
}

int main(){
// 智能指针(smart pointer),定义于 memory.h
	// shared_ptr	允许多个指针指向同一个对象	
	// unique_ptr	独占所指向的对象
	// week_ptr		弱引用,指向 shared_ptr 所管理的对象
	
	// shared_ptr unique_ptr 都支持的操作
	// 1.创建时必须指明一个指针可以指向的类型,默认初始化为空指针
	shared_ptr< string> sp1, sp2;
	unique_ptr< string> up;
	// 2.用作判断条件,若指向一个对象,则为 true
	if( sp1 && sp1->empty()){	//4.调用指向对象的成员
		// 3.解引用,获得指向对象的左值
		*sp1 = "hello";
		// 6.返回智能指针中保存的指针
		cout << *( sp1.get()) << endl;
	}
	// 5.交换两个指针
	swap( sp1, sp2);
	sp1.swap( sp2);
	
	
	// shared_ptr 独有的操作
	// 1> make_shared 返回一个 shared_ptr,指向一个动态分配的类型为 string的对象,并初始化为 world
	// 最安全的分配和使用动态内存的方法,初始化的参数必须是指定类型的构造函数的参数
	sp2 = make_shared< string>( "world");
	// 每个shared_ptr 都有一个引用计数(reference count),当拷贝时,计数器增加,赋值或销毁时,计数器减少,当变为0时,自动释放所管理的对象
	// 2> sp3是 sp2 一个拷贝,同时增加 sp2的计数器,sp2 的指针必须能转换为 string *
	shared_ptr< string> sp3( sp2);
	// 3> sp1 和 sp3 所保存的指针必须能够相互转换,同时递减sp1计数,若为0,则释放其管理的内存,递增 sp3 的计数
	sp1 = sp3;
	// 4> 如果 use_count() 为1, 则unique() 为true,否则为 false
	if( sp3.unique())
		cout << "count == 1" << endl;
	else
		// use_count() 返回与 sp3 共享对象的智能指针数量,比较慢,主要用于测试
		cout << "count : " << sp3.use_count() << endl;
		
	// 使用动态内存的情况
		// 不知道要使用多少对象
		// 不知道对象的确切类型
		// 在多个对象之间共享数据
	StrBlob sb1, sb2{"hello", "world"};	
	cout << "sb1.count = " << sb1.size() << endl;
	cout << "sb2.count = " << sb2.size() << endl;
	{
		StrBlob sb3{"test"};
		sb1 = sb3;
		cout << "sb3.count = " << sb3.size() << endl;
		cout << "sb1.count = " << sb1.size() << endl;
		sb3.push_back( "strblob");
		cout << "sb3.count = " << sb3.size() << endl;
		cout << "sb1.count = " << sb1.size() << endl;
	}
	const StrBlob sb4{ "say", "yeah!~"};
	cout << sb4.front() << " < > " << sb4.back() << endl;
	cout << sb2.front() << " < > " << sb2.back() << endl;
	cout << "sb1.count = " << sb1.size() << endl;
	cout << "sb2.count = " << sb2.size() << endl;
	sb1 = sb2;
	cout << "sb1.count = " << sb1.size() << endl;
	cout << "sb2.count = " << sb2.size() << endl;
	
	// 直接管理内存 new 分配内存,delete 释放 new 分配的内存
	// 注意区别
	int *pi1 = new int;			// 默认初始化,对于内置类型,其值是未定义的
	int *pi2 = new int();		// 值初始化
	auto *pi3 = new auto(5);	// 自动推断类型
	const int *pi4 = new const int(1024);	// const 对象必须初始化,如果类类型有默认构造函数可以隐式初始化
	
	cout << *pi1 << " - " << *pi2 << " - " << *pi3 << " - " << *pi4 << endl;
	
	// 如果内存耗尽,new 表达式失败,会抛出 bad_alloc 异常
	// bad_alloc nothrow new 定义于 new.h
	int *pi5 = new (nothrow) int();	// 定位 new (placement new),如果分配失败,不会抛出异常,返回一个空指针
	
	// 释放动态内存 (delete expression)
	// 释放一块并非 new 分配的内存或将相同的指针释放多次,其行为是未定义的
	delete pi5;	// 两步,销毁指向的对象,释放对应内存,pi5必须是指向动态分配的内存或是一个空指针
	
	// 动态内存有效期直到显式释放
	// 如果使用了返回动态内存指针的函数,使用者使用完必须显式释放
	// 常见问题:
		// 1.忘记 delete 分配的内存,很难检测,多数情况下,等内存耗尽才发现
		// 2.使用已经释放掉的对象,虽然指针已失效但多数情况下仍保存了原地址,类似于未定义的指针,建议置为空指针,
		// 	但是如果有多个指针指向该内存,逐个释放也很麻烦
		// 3.释放已经释放过的内存,未定义行为
	auto pvi = get_pvi();
	save_pvi( pvi);
	print_pvi( pvi);
	delete pvi;
	
	// 智能指针的构造函数是 explicit, 不能进行隐式转换,只能使用直接初始化
	// 错误:shared_ptr< string> ps1 = new string("hello world");
	shared_ptr< string> ps2( new string("hello world"));	
	
	// 定义改变 shared_ptr 的其他方法
		// shared_ptr< T> p( q)  	p 管理 q指向的对象,q必须指向 new 分配的内存,且能够转换为 T* 类型
		// shared_ptr< T> p( u)		p 从 unique_ptr u 那里接管了对象的所有权,将 u 置空
		// shared_ptr< T> p( q, d)	p 从 内置类型指针 q 那里接管了对象的所有权,q 必须能转换为 T*, p 将使用可调用对象 d 代替 delete
		// shared_ptr< T> p( p2, d)	p 是 shared_ptr p2 的拷贝, 但 p 可以使用可调用对象 d 代替 delete
		// p.reset()				若 p 是唯一指向其对象的 shared_ptr,reset会释放此对象
		// p.reset( q)				若同时传递了可选的内置指针 q,会另 p 指向 q, 否则为空
		// p.reset( q, d)			若同时还传递了 d,使用可调用对象 d 代替 delete
		
	// 不要使用 get() 初始化另一个智能指针或为智能指针赋值
	shared_ptr< int> spi1( new int( 99));
	// pi1 和 spi1 指向同一个内存
	int *pi1 = spi1.get();
	{
	// pi1 spi2 和 spi1 指向同一个内存,但分别是2个不同的智能指针,且计数都为1,当spi2 脱离作用域,其指向的内存会被释放,后续的解引用行为未定义
		shared_ptr< int> ( spi2);
	}
	int vspi1 = *spi1;	//未定义行为
	
	// 智能指针使用规范:
		// 不使用相同的内置指针初始化多个智能指针
		// 不 delete get() 返回的指针
		// 不使用 get() 初始化或reset 另一个智能指针
		// 如果使用了 get() 指针,当其最后一个对应的智能指针销毁后,指针变为无效
		// 如果使用的智能指针管理的资源不是 new 分配的内存,记住传递一个删除器
	
	
	// unique_ptr
		
	// 定义改变 unique_ptr 的其他方法
		// unique_ptr< T> p  	指向 T 类型的对象, 空 unique_ptr, 可以使用 delete 释放
		// unique_ptr< T, D> p		同上,使用 D 类型的可调用对象来释放指针
		// unique_ptr< T, D> p ( d)		同上,使用 D 类型的可调用对象 d来释放指针
		// p = nullptr				释放 p 指向的对象,将 p 置空
		// p.release()					放弃控制权,返回指针,将 p 置空
		// p.reset()				释放此对象
		// p.reset( q)				若同时传递了可选的内置指针 q,会另 p 指向 q, 否则为空
		// p.reset( nullptr)			
		
		// 不支持赋值和拷贝, 可以通过 release() 或 reset() 转移控制权
		unique< int> upi1;
		unique< int> upi2 ( new int ( 99));
		upi1.reset( upi2.release());
		
	// weak_ptr
		// 指向 shared_ptr 关联的对象,但不增加其引用计数,当对象最后一个 shared_ptr 被销毁,即使有 weak_ptr 仍会销毁
		
		// weak_ptr< T> w		空 weak_ptr 指向类型 T 的对象
		// weak_ptr< T> w( sp)	与 shared_ptr sp 指向同一个对象, T 必须能转换为 sp 指向的类型
		// w = p				p 可以是 shared_ptr	或 weak_ptr,赋值后共享对象
		// w.reset()			置空 w
		// w.use_count()		与 w 共享对象的 shared_ptr 数量
		// w.expired()			若 w.use_count() 为0,返回 true,否则为 false
		// w.lock()				如果 w.expired() 为真,返回空 shared_ptr, 否则返回指向 w 对象的 shared_ptr
		
	return 1;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值