C++基础(十一)智能指针之unique_ptr

前一篇文章介绍了共享指针shared_ptr,这篇介绍另一种智能指针:unique_ptr。

1、创建

与shared_ptr不同,C++11并没有提供类似std::make_shared的标准库函数来返回一个unique_ptr,但是C++14提供了类似的库函数:std::make_unique,语法如下:

std::make_unique<类型>(参数列表)

依然以Person类为例:

class Person
{
public:
	Person(const std::string& strName, int iAge) :m_strName(strName), m_iAge(iAge){}
	~Person(){ std::cout << "Person析构, name=" << m_strName << std::endl; }

	void PrintInfo(){ std::cout << "姓名:" << m_strName << ", 年龄:" << m_iAge << std::endl; }
private:
	std::string m_strName;
	int m_iAge;
};

主函数如下:

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
		p1->PrintInfo();
	}

	system("pause");
	return 0;
}

执行结果:

C++基础(十一)智能指针之unique_ptr

很简单!一旦p1的作用域消失,便会自动释放内存。

2、所有权转移及内容清空

从unique的字面就能理解,这种类型的指针,不能与其他指针一起共享内存对象,只能唯一。如果真的需要,只能将所有权转移,转移后原来的指针无效。

转移所有权,有两种方式:

(1)p.release()

p为unique_ptr指针,放弃对指针的控制权,返回指针,并将p置为空。

看代码:

int _tmain(int argc, _TCHAR* argv[])
{
	{
		Person* p = nullptr;
		{
			std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
			p = p1.release();
			if (p1)
				std::cout << "p1非空" << std::endl;
			else
				std::cout << "p1已空" << std::endl;
		}
		p->PrintInfo();
	}

	system("pause");
	return 0;
}

执行结果如下:

C++基础(十一)智能指针之unique_ptr

这里,在局部范围内定了一个普通的指针p,接着创建一个unique_ptr指针p1,紧接着用p1.release(注意这里是".",不是"->")将p1的控制权转移给p。可以看到,当p1的作用域结束后,并没有自动释放对象的内存。这是因为,在经过release后,p1原来的内存对象已经不再和智能指针绑定,也就无法通过智能指针自动释放。

当p的作用域结束后,也没有自动释放对象。这是因为p是普通指针,虽然它的对象是从智能指针来的,但是想要释放,还是需要手动delete,如果把p改成智能指针就可以做到自动释放。

但是需要注意一点,release返回是普通指针,如果要用智能指针接收,则需要转换一次。

代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::unique_ptr<Person> p;
		{
			std::unique_ptr<Person> p1 = std::make_unique<Person>("ye", 30);
			p = std::unique_ptr<Person>(p1.release());  //这里不能直接p = p1.release();
		}
		p->PrintInfo();
	}	

	system("pause");
	return 0;
}

执行结果如下:

C++基础(十一)智能指针之unique_ptr

这里,用unique_ptr的指针p来接收p1所指向的对象,当p的作用域消失,则会自动释放内存,调用析构函数,打印相关信息。

(2)p.reset()/p.reset(k)

p为unique_ptr指针,若不传参数,则直接将p置空;若提供了普通指针k,则将p指向k的内存。

看代码:

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::unique_ptr<Person> p = std::make_unique<Person>("zhao", 40);
		{
			std::unique_ptr<Person> k = std::make_unique<Person>("ye", 30);
			p.reset(k.release());  //这里p原本指向的对象"zhao"不再有智能指针指向它,所以会自动释放
			if (k)
				std::cout << "k不为空" << std::endl;
			else
				std::cout << "k为空" << std::endl;
		}
		p->PrintInfo();
		p.reset();
		if (p)
			std::cout << "p不为空" << std::endl;
		else
			std::cout << "p为空" << std::endl;
	}

	system("pause");
	return 0;
}

执行结果如下:

C++基础(十一)智能指针之unique_ptr

程序中,先创建了一个对象为"zhao"的unique_ptr指针p,紧接着创建一个对象为"ye"的unique_ptr指针k,再执行语句p.reset(k.release())。此时,k的对象所有权会交给p,而p原来指向的对象因为再没有任何智能指针指向它,所以会自动释放,于是打印了"zhao"的析构信息。k调用了release,已经为空,用if判断可以验证。紧接着打印p的信息,为之前k的对象"ye";再执行p.reset(),未传任何参数,所以p直接被置空,用if判断可以验证。

3、局部unique_ptr,可以作为返回值返回

这个特征和unique_ptr不能赋值有一些冲突,但是仔细想想也很合理。

局部的unique_ptr在返回时,代表其即将消失,作为返回值返回后,指针依然只有一份,完全符合unique的特性。代码如下:

std::unique_ptr<Person> GetPerson(const std::string& strName, int iAge)
{
	std::unique_ptr<Person> p = std::make_unique<Person>(strName, iAge);
	return p;
}

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::unique_ptr<Person> p = GetPerson("ye", 30);
		p->PrintInfo();
	}

	system("pause");
	return 0;
}

执行结果:

C++基础(十一)智能指针之unique_ptr

4、不能直接作为参数,需要以引用/const引用的方式作为参数

unique_ptr不能直接作为参数,需要用引用。

函数定义时没问题,但是一旦调用就会编译报错。

看代码:

void PrintInfo(std::unique_ptr<Person> pPerson)  //这里直接用的是unique_ptr
{
	pPerson->PrintInfo();
}

int _tmain(int argc, _TCHAR* argv[])
{
	{
		std::unique_ptr<Person> p = std::make_unique<Person>("ye", 30);
		PrintInfo(p);  //如果将这一句注释掉,编译会成功
	}

	system("pause");
	return 0;
}

编译结果如下(vs2013):

C++基础(十一)智能指针之unique_ptr

修改PrintInfo函数,如下:

void PrintInfo(std::unique_ptr<Person>& pPerson)  //参数改成了引用
{
	pPerson->PrintInfo();
}

主函数不变,运行结果如下:

C++基础(十一)智能指针之unique_ptr

至此,C++11提出来的两个简单方便易用的智能指针shared_ptr和unique_ptr介绍完毕。其实都很简单,多用几次就会。

上一篇:信用卡模型(三)


下一篇:SQL 表的知识