首页 > 代码库 > C++ Primer笔记8_动态内存_智能指针

C++ Primer笔记8_动态内存_智能指针

1.动态内存

C++中,动态内存管理是通过一对运算符完成的:new和delete。C语言中通过malloc与free函数来实现先动态内存的分配与释放。C++中new与delete的实现其实会调用malloc与free。

new分配:

分配变量空间:

int *a = new int; // 不初始化
int *b = new int(10); //初始化为10
string *str = new string(10, );

分配数组空间:

int *arr = new int[10];//分配的数组空间未初始化!

delete释放:

delete a;
delete [] arr;//delete的对象是数组

2.智能指针

       由于 C++ 语言没有自动内存回收机制,每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见。

       用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法。包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr。

       C++11新标准提供了两种智能指针,负责自动释放所指向的对象。shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。标准库还定义了一个名为weak_ptr的伴随类;这三种类型都定义在memory头文件中。


    对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。

       访问智能指针包含的裸指针则可以用 get() 函数。由于智能指针是一个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要这样判断:if (my_smart_object.get())。

       智能指针包含了 reset() 方法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。


2.1 auto_ptr

#include <iostream>
#include <memory>
using namespace std;

class A
{
public:
	A(int n, string s):num(n), str(s)
	{
	}
	~A()
	{
		cout << "~A : " << num << " " << str  << endl;
	}
	void print()
	{
		cout << "num: " << num << ", str: " << str << endl;
	}
	void setNum(int n)
	{
		num = n;
	}
	void setString(string s)
	{
		str = s;
	}
private:
	int num;
	string str;
};

int main()
{
	auto_ptr<A> my(new A(10, string(10, 'n')));
	if(my.get())
	{
		my->print();
		(*my).setNum(20);
		my.get()->setString(string("SCOTT"));
		my->print();
	}
	auto_ptr<A> my1 = my;

	cout << my.get() << "------" << my1.get()<< endl;
	//my->print();	segment default!

	return 0;
}

运行结果:

num: 10, str: nnnnnnnnnn
num: 20, str: SCOTT
0------0x9cd2028 ——
赋值后所有权转移到my1 ,原来的my相当于野指针!
~A : 20 SCOTT


上述程序最后如果加上

my.release();
my1.release();

则会发现析构函数不会被执行! 说明release函数不会释放对象。


总结:std::auto_ptr 可用来管理单个对象的对内存,但是,请注意如下几点:

(1)    尽量不要使用“operator=”。如果使用了,请不要再使用先前对象。

(2)    记住 release() 函数不会释放对象,仅仅归还所有权。

(3)    std::auto_ptr 最好不要当成参数传递(读者可以自行写代码确定为什么不能)。

(4)    由于 std::auto_ptr 的“operator=”问题,有其管理的对象不能放入 std::vector 等容器中。

由于 std::auto_ptr 引发了诸多问题,一些设计并不是非常符合 C++ 编程思想,所以引发了下面 boost 的智能指针,boost 智能指针可以解决如上问题。


2.2 boost::scoped_ptr

    boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。boost::scoped_ptr  std::auto_ptr 一样,可以方便的管理单个堆内存对象,特别的是,boost::scoped_ptr 独享所有权,避免了 std::auto_ptr恼人的几个问题。

#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;

class A
{
public:
	A(int n, string s):num(n), str(s)
	{
	}
	~A()
	{
		cout << "~A : " << num << " " << str  << endl;
	}
	void print()
	{
		cout << "num: " << num << ", str: " << str << endl;
	}
	void setNum(int n)
	{
		num = n;
	}
	void setString(string s)
	{
		str = s;
	}
private:
	int num;
	string str;
};

int main()
{
	scoped_ptr<A> my(new A(10, string(10, 'n')));
	if(my.get())
	{
		my->print();
		(*my).setNum(20);
		my.get()->setString(string("SCOTT"));
		my->print();
	}
//	scoped_ptr<A> my1 = my;	 error!  scoped_ptr 没有重载operator=,不会导致所有权转移  scoped_ptr也没有定义release函数
	cout << my.get() << "------" << my.get()<< endl;

	return 0;
}


2.3 boost::shared_ptr

     boost::shared_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。在上面我们看到 boost::scoped_ptr 独享所有权,不允许赋值、拷贝,boost::shared_ptr 是专门用于共享所有权的,由于要共享所有权,其在内部使用了引用计数boost::shared_ptr 也是用于管理单个堆内存对象的。

#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;

class A
{
public:
	A(int n, string s):num(n), str(s)
	{
	}
	~A()
	{
		cout << "~A : " << num << " " << str  << endl;
	}
	void print()
	{
		cout << "num: " << num << ", str: " << str << endl;
	}
	void setNum(int n)
	{
		num = n;
	}
	void setString(string s)
	{
		str = s;
	}
private:
	int num;
	string str;
};

void test(shared_ptr<A> &test)
{
	test->print();
	return ;
}

int main()
{
	shared_ptr<A> my(new A(10, string(10, 'n')));
	if(my.get())
	{
		my->print();
		(*my).setNum(20);
		my.get()->setString(string("SCOTT"));
		my->print();
	}
	cout << "my.use_count: " << my.use_count() << endl;
	test(my);
	cout << "my.use_count: " << my.use_count() << endl;

//	cout << my.get() << "------" << my1.get()<< endl;
	//my->print();	segment default!

	return 0;
}
运行结果:

num: 10, str: nnnnnnnnnn
num: 20, str: SCOTT
my.use_count: 1
num: 20, str: SCOTT
my.use_count: 1
~A : 20 SCOTT


     boost::shared_ptr 也可以很方便的使用。并且没有 release() 函数。关键的一点,boost::shared_ptr 内部维护了一个引用计数,由此可以支持复制、参数传递等。boost::shared_ptr 提供了一个函数 use_count() ,此函数返boost::shared_ptr 内部的引用计数。可以看到在test函数返回后,引用计数又降低为1;当我们需要使用一个共享对象的时候,boost::shared_ptr 是再好不过的。


2.4 weak_ptr

boost::weak_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。

在讲 boost::weak_ptr 之前,让我们先回顾一下前面讲解的内容。似乎boost::scoped_ptr、boost::shared_ptr 这两个智能指针就可以解决所有单个对象内存的管理了,这儿还多出一个 boost::weak_ptr,是否还有某些情况我们没纳入考虑呢?

回答:有。首先 boost::weak_ptr 是专门为 boost::shared_ptr 而准备的。有时候,我们只关心能否使用对象,并不关心内部的引用计数。boost::weak_ptr 是 boost::shared_ptr 的观察者(Observer)对象,观察者意味着 boost::weak_ptr 只对 boost::shared_ptr 进行引用,而不改变其引用计数,当被观察的 boost::shared_ptr 失效后,相应的 boost::weak_ptr 也相应失效。


写到这,先告一段落,理论知识是参考网上的博文,然后自己实现一些demo加深理解。