C++智能指针unique_ptr

C++11:unique_ptr、shared_ptr、weak_ptr,本节介绍一下unique_ptr

1.智能指针产生原因

众所周知,C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。然而,很多程序员在使用堆内存时都很头疼,因为一次不恰当的使用方法,很容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等。所以C++引入了智能指针,使用使用智能指针能更好的管理堆内存。

2.理解智能指针原理

1)智能指针是利用了一种叫做RAII技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。

补充:RAII技术即资源分配及初始化,使用类来封装资源的分配和初始化,构造函数完成资源的分配和初始化,析构函数完成资源的清理,可以保证正确的初始化和资源释放。

2)智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。千万不要用一块非new分配的动态内存去初始化一个智能指针,因为自行释放非堆地址很有可能出现问题。

3)智能指针还有一个作用是把值语义转换成引用语义。

C++和Java有一处最大的区别在于语义不同,在Java里面,下列代码: 

Student s1 = new Student ();

Student s2 = s1;

 这里其实只生成了一个对象,s1和s2仅仅是共享对象的引用而已。但在C++中不是这样的,

  Student s1;

  Student s2 = s1;

  这里却是就是生成了两个对象。

3.包含头文件

C++98版的auto_ptr和C++11版的unique_ptr、shared_ptr、weak_ptr,都包含在头文件<memory>中,隶属于std命名空间中。

4.unique_ptr

unique_ptr持有对对象的独有权——两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作。但支持通过std::move转移内部指针所有权。

unique_ptr的成员函数:

1)get 获得内部对象的指针, 由于已经重载了()方法, 因此和直接使用对象是一样的.如 unique_ptr<int> sp(new int(1)); sp 与 sp.get()是等价的;
2)release 放弃内部对象的所有权,将内部指针置为空, 返回所内部对象的指针, 此指针需要手动释放;
3)reset 销毁内部对象并接受新的对象的所有权(如果使用缺省参数的话,也就是没有任何对象的所有权, 此时仅将内部对象释放, 并置为空);

unique_ptr<int> up1(new int);	// ok
up1 = nullptr;	//显式销毁所指对象,同时智能指针变为空指针。与u_s2.reset()等价

4)swap 交换两个unique_ptr对象(即交换所拥有的对象);

unique_ptr的用法:

1)unique_ptr对象不能进行拷贝和赋值操作,只能进行移动操作

unique_ptr<int> up1(new int);		// ok
unique_ptr<int> up2 = up1;		// 编译错误
unique_ptr<int> up3(up1);		// 编译错误
unique_ptr<int> up4 = move(up1);	// ok,up1变成空指针

查看拷贝构造和赋值源码,可以看到它们都被标记为已删除的函数了,在编译上面的代码时,会报编译错误:

unique_ptr(const _Myt&) = delete;
_Myt& operator=(const _Myt&) = delete;

当然,有些博主解释为,它们被设成了不可访问的私有函数。 

虽然 unique_ptr 不支持拷贝和赋值, 但是可以调用 release 或 reset 将指针的所有权从一个(非 const) unique_ptr 转移到另一个。

std::unique_ptr<int> up1(new int(1));
std::unique_ptr<int> up2(up1.release()); // up1 放弃内部对象的所有权,将内部指针置为空
std::unique_ptr<int> up3(new int(2));
up3.reset(up2.release());	//up3销毁内部对象,并接受新的对象的所有权

2)源unique_ptr是临时右值时,编译器允许将源unique_ptr临时右值赋给另一个unique_ptr对象

// 可以进行移动构造
template <class Ty>
std::unique_ptr<Ty> Clone(const Ty& obj)
{
	std::unique_ptr<Ty> temp = std::unique_ptr<Ty>(new Ty(obj)); // 编译器允许,源右值对象是临时右值
	return temp;	// 对象的所有权被传递出Clone()
}

unique_ptr<int> pu1 = Clone(5);// 可以进行移动赋值,Clone函数返回值也是临时右值
unique_ptr<int> pu2;
pu2 = pu1;                          // pu1不是临时右值,编译器不允许
unique_ptr<int> pu3;
pu3 = unique_ptr<int>(new int(2));  // 编译器允许,源右值对象是临右值

应该是编译器知道要临时右值的unique_ptr对象即将被销毁, 因此执行一种特殊的"拷贝"。

3)两个unique_ptr不能指向一个对象

int *iPtr = new int(0);
std::unique_ptr<int> up1(iPtr);
std::unique_ptr<int> up2(iPtr);// 编译可以通过,但运行时发生错误

up2和up1都指向了iPtr,将来iPtr会被释放两次,造成错误。

4)unique_ptr支持管理动态数组

因为unique_ptr有unique_ptr<X[]>重载版本,销毁动态对象时调用delete[]

unique_ptr<int[]> p (new int[3]{1,2,3});  
p[0] = 0;// 重载了operator[]

5)可以使用unique_ptr的移动语义将对象插入容器中

unique_ptr<int> pu1(new int(1));
vector<unique_ptr<int> > vect;
vect.push_back(pu1);			// 编译错误
vect.push_back(std::move(pu1));	// OK,使用移动语义,pu1自己就不把持对象所有权了

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值