C++中的动态分配(《C++primer》笔记心得)

在C++中初步阶段中最难理解的估计就是这个,对程序中各部分的储存方式和位置也是从这里开始探究的,现在照着这本书,把感觉有意义重要的地方再说一般。以后可别忘了,不怕学不会,就怕学会了,然后忘了。

1.new的操作很可能会失败,很可能会失败,很可能会失败呀,这下面的代码在我的电脑失败了。提示了一下bad_alloc异常。

int main()
{
	int *temp;
	for (long long i = 0; i < 100000000; ++i)
	{
		temp = new int;
	}
	system("pause");
	return 0;
}

2.delete前没有必要判断是否该指针是否为NULL,如果是NULL,delete不会调用操作符delete(),它自己会进行判断,所以delete后的指针置为NULL不会引起二次释放的问题,这也是为什么要delete后的指针要置为NULL的原因,不会被二次释放(我是这么想的)。

3.指针与被指向的内存的生存期不一定是相同的,delete该指针后,该内存就被释放了。没有置为NULL的情况下,该指针任然指向无效的的内存,该指针本身的生命期由她自己的特性所决定。

4.指向同一个内存的其中一个指针被delete后,该内存就被回收了,其他原本指向这个内存的指针此时指向的也是无效内存,所以这个问题也需要注意。

为了应对C++中麻烦的内存管理,防止总是出现失误,C++标准库提供了auto_ptr类模板。用来管理被new分配的内存,但只能管理单个对象,像new一个数组就不能进行管理了,说是会发生未定义的行为(我觉得是设计语言的嫌麻烦没做,或者做了有点鸡肋)。auto_ptr对象指向被new分配的内存对象,当auto_ptr对象的生命期结束的时候,这块内存也将被回收。想要使用这个类模板,要包括#include <memory>这个头文件。auto_ptr有三种形式。
auto_ptr< type_pointed_to > identifier( ptr_allocated_by_new );
auto_ptr< type_pointed_to > identifier( auto_ptr_of_same_type );
auto_ptr< type_pointed_to > identifier;

简单来说,上面的三种类型就是用new出来的内存、其他同类型的auti_ptr对象来初始化一个auto_ptr对象,以及没有初始化auto_ptr对象,下面的代码中都会涉及到这三种形式。

int main()
{
    //auto_ptr 的对象为ptr,指向了new分配的一段内存,且用jijiji来初始化这段内存
	auto_ptr<string> ptr(new string("jijiji"));
	cout << *ptr << endl;
	system("pause");
	return 0;
}

type_pointed_to 代表由 new 表达式创建的对象的类型,比如int,double还有自定义的类型……根据一般的常识,你可能会认为这种额外的安全性一定来自于执行效率的开销,但实际情况并不是这样,因为对这些操作的支持都是内联的,它们由编译器在调用点上展开,所以使用 auto_ptr 对象并不比直接使用指针代价更高(书上原话)。

避免了内存没有释放,但还有一个问题,就是指向同一个内存的不同指针的释放问题,auto_ptr也避免了二次释放,怕你不释放,又怕你多释放,这二次释放比不释放还恐怖,不释放暂时可能没事,多次释放往往程序当场凉了。auto_ptr不需要自己delete,当然也避免了这个问题

int main()
{
	auto_ptr<string> ptr(new string("jijiji"));	
	auto_ptr<string> ptr1(new string("484848"));
	ptr1 = ptr;
	cout << *ptr << endl;
	system("pause");
	return 0;
}

ptr与ptr1指向不同的内存,在ptr赋值给ptr1时, ptr不在有这个内存的拥有权(这个拥有权应该就是ptr不指向原先的内存,源代码里面也是这样的),ptr1指向ptr的内存,而ptr1也不再指向原本的内存(被释放了,在下面的源代码中可以看到),最终没有内存泄露和重复释放的问题发生。可又碰到新的问题了,给你一个auto_ptr对象你是不能直接判断它有没有指向一段内存,不能通过ptr==NULL来判断是否指向一段内存(好像是没有重载==运算符吧,源代码找了一下,没看到呀)。它自己也提供了类似的机制来做这件事。

int main()
{
	auto_ptr<string> ptr(new string("jijiji"));
	
	auto_ptr<string> ptr1;
	if (ptr1.get() == NULL)
	{
		cout << "ptr1 是空" << endl;
	}
	cout << *ptr << endl;
	system("pause");
	return 0;
}

上面的代码就得到正确的输出结果,找到了没初始化的auto_ptr对象,不就是给他赋值嘛,要不然那么闲?当然未初始化的auto_ptr也不能像指针那样直接赋值,我截取了一段重载=的源代码,这里是用另一个对象来初始化一个对象(不一定是这一段,我感觉这段最符合逻辑,最靠谱,水平还是菜,看不懂……)。

auto_ptr& operator=(auto_ptr_ref<_Ty> _Right) noexcept
		{	// assign compatible _Right._Ref (assume pointer)
		_Ty * _Ptr = _Right._Ref;
		_Right._Ref = 0;	// release old
		reset(_Ptr);	// set new
		return (*this);
		}

可以看到其中有一个reset这个函数,它就是用来重置一个auto_ptr对象的。看下面的代码。

int main()
{
	auto_ptr<string> ptr(new string("jijiji"));
	
	auto_ptr<string> ptr1;
	ptr1 = ptr;   //这是不允许的,没重载=的这种用法(我猜的)
	ptr1.reset(new string("kokoko"));   //正确的用法

	cout << *ptr << endl;
	system("pause");
	return 0;
}

用reset就成功地重置了未初始化的对象。已初始化的auto_ptr对象用reset会造成内存泄漏的,因为你没有像源代码里面那样先将原本的内存释放,直接将新分配的内存给了它。想深入了解auto_ptr还是看源代码吧,就像侯捷大师说的那样,“源码之前,了无密码”。

auto_ptr的确带给了我们很大的方便,但同时也存在陷阱。书中给力几点警告。

1.我们必须小心,不能用一个指向内存不是通过应用 new 表达式分配的指针来初始化或赋值 auto_ptr 如果这样做了,delete 表达式会被应用在不是动态分配的指针上,这将导致未定义的程序行为。
2.我们必须小心,不能让两个 auto_ptr 对象拥有空闲存储区内同一对象的所有权,一种很显然犯这种错误的方法是用同一个指针初始化或赋值两个 auto_ptr 对象;另一种途径是通过使用 get()操作,例如:

auto_ptr< string >
pstr_auto( new string( "Brontosaurus" ) );
// 现在两个指针都指向同一个对象
// 并都拥有该对象的所有权
auto_ptr< string > pstr_auto2( pstr_auto.get() );


release()操作允许将一个 auto_ptr 对象的底层对象初始化或赋位给第二个对象,而不会使两个 auto_ptr 对象同时拥有同一对象的所有权 release()不仅像 get()操作一样返回底层对象的地址,而且还释放这对象的所有权,前面代码段可被正确改写如下:
 

// 两个对象仍然指向同一个对象
// 但是, pstr_auto 不再拥有拥有权
auto_ptr< string >
pstr_auto2( pstr_auto.release() );

完事了,感觉有些地方错了,但又不确定,欢迎有不同看法同学的评论留言,希望有大佬可以指正批评!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值