是STL中的智能指针家族的成员之一, 它管理由
new exPRession获得的对象,在
auto_ptr对象销毁时,他所管理的对象也会自动被
delete` 掉。auto_ptr的拷贝构造函数和拷贝赋值会改变 right hand value,并且拷贝的副本不会等于原始的、被拷贝的那个
auto_ptr对象的值。(实际上,
auto_ptr` 的拷贝构造函数和拷贝赋值函数会让 left hand value 接管 right hand value 所管理的对象。)由于不一样的拷贝语义, auto_ptr
不适用于标准容器, 因此,更建议使用std::unique_ptr
。(1) 构造 auto_ptr
对象, 让它管理 p
指向的对象。 (2) 构造 auto_ptr
对象,让它接管 r
管理的对象。实际上新的 auto_ptr
对象是靠 r.release()
函数获得管理权的。因此,r
失去了管理权。 (3) 这个构造函数和 (2) 类似, 主要针对能隐式转换为 T*
类型的 Y*
。 (4) 构造 auto_ptr
对象, 让它接管 auto_ptr_ref<Y>
类型的 m
管理的对象。而m
是通过 p.release()
从 auto_ptr
对象 p
中获取管理权的。
Q: what is auto_ptr_ref, what it achieves and how it achieves it ?
A: It is rather confusing. Basically, auto_ptr_ref exists because the auto_ptr copy constructor isn’t really a copy constructor in the standard sense of the Word.
Copy constructors typically have a signature that looks like this:
X(const X &b); The auto_ptr copy constructor has a signature that looks like this:
X(X &b) This is because auto_ptr needs to modify the object being copied from in order to set its pointer to 0 to facilitate the ownership semantics of auto_ptr.
Sometimes, temporaries cannot match a copy constructor that doesn’t declare its argument const. This is where auto_ptr_ref comes in. The compiler won’t be able to call the non-const version of the copy constructor, but it can call the conversion Operator. The conversion operator creates an auto_ptr_ref object that’s just sort of a temporary holder for the pointer. The auto_ptr constructor or operator = is called with the auto_ptr_ref argument.
If you notice, the conversion operator in auto_ptr that automatically converts to an auto_ptr_ref does a release on the source auto_ptr, just like the copy constructor does.
It’s kind of a weird little dance that happens behind the scenes because auto_ptr modifies the thing being copied from.
简单地总结: auto_ptr_ref
主要解决用右值来构造 auto_ptr
的情况。 因为, auto_ptr(auto_ptr& r)
构造函数只能以左值引用做参数。当右值来构造 auto_ptr_ref
的时候,实际上实现过程如下(这其实是移动语义的早期实现版本):
auto_ptr
的拷贝赋值函数会让 left hand value 接管 right hand value 所管理的对象。
(1) 将该对象隐式转换为 auto_ptr_ref<Y>
类型。 (2) 将该对象隐式转换为 auto_ptr<Y>
类型。
(1) 返回该 *this
所管理对象的指针。 (2) 返回该 *this
所管理对象。 (3) 返回该 *this
所管理对象的指针。 (4) 让 *this
管理 p
所指向的对象,如果 *this
已有管理的对象,则先 delete
掉当前管理的对象。 (5) 移交出 *this
所管理对象的管理权。返回 *this
所管理对象的指针,并将 *this
内部的指针置为空。
代码
#include <iostream>#include <string>#include <memory>using namespace std;// 展示测试结果 template<class Ty>void Test(auto_ptr<Ty>& showPtr, string name, string hint){ cout << hint; if(showPtr.get() == nullptr) cout << name << ".get() == nullptr" << endl; else cout << "*" << name << ".get() == " << *showPtr.get() << endl; }// for test...class Base{public: Base(double pi = 0.0) : m_pi(pi){ //... } virtual void ShowName() const { cout << "Base Object"; } double m_pi;};class Derive : public Base{public: virtual void ShowName() const { cout << "Derive Object"; }};ostream& operator<<(ostream& os, const Base& b){ b.ShowName(); return os;}ostream& operator<<(ostream& os, const Derive& b){ b.ShowName(); return os;}int main(){ // 构造函数... // explicit auto_ptr(X* p = 0); (1) auto_ptr<int> intPtr1; Test(intPtr1, "intPtr1", "explicit auto_ptr(X* p = 0).../n"); int* ptr = new int(2); auto_ptr<int> intPtr2(ptr); Test(intPtr2, "intPtr2", ""); Derive* dp = new Derive; auto_ptr<Derive> dptr(dp); Test(dptr, "dptr", ""); cout << endl; // auto_ptr(auto_ptr& r); (2) auto_ptr<int> intPtr3(intPtr2); Test(intPtr3, "intPtr3", "auto_ptr(auto_ptr& r).../n"); Test(intPtr2, "intPtr2", "intPtr2 失去了对 ptr 的控制权: "); cout << endl; // template<class Y> (3) // auto_ptr<auto_ptr<Y>& r); auto_ptr<Base> bPtr(dptr); Test(bPtr, "bPtr", "template<class Y> auto_ptr<auto_ptr<Y>& r).../n"); Test(dptr, "dptr", "dptr 失去了对 dp 的控制权: "); cout << endl; // template<class Y> (4) // auto_ptr(auto_ptr_ref<Y> m); auto_ptr_ref<string> ptrRef(new string("many strings")); auto_ptr<string> strPtr(ptrRef); Test(strPtr, "strPtr", "template<class Y> auto_ptr(auto_ptr_ref<Y> m).../n"); cout << endl; // 拷贝赋值函数 // auto_ptr& operator=(auto_ptr& r); (1) auto_ptr<int> intPtr4; intPtr4 = intPtr3; Test(intPtr4, "intPtr4", "auto_ptr& operator=(auto_ptr& r).../n"); Test(intPtr3, "intPtr3", "intPtr3 失去了对 ptr 的控制权: "); cout << endl; // template<class Y> (2) // auto_ptr& operator=(auto_ptr<Y>& r); auto_ptr<Derive> derivePtr(new Derive); Test(derivePtr, "derivePtr", "template<class Y> auto_ptr& operator=(auto_ptr<Y>& r).../n"); auto_ptr<Base> basePtr; basePtr = derivePtr; Test(basePtr, "basePtr", "basePtr 获得了控制权....../n"); Test(derivePtr, "derivePtr", "derivePtr 失去了控制权.../n"); cout << endl; // auto_ptr& operator=(auto_ptr_ref m); (3) auto_ptr_ref<string> strPtrRef(new string("auto_ptr_ref strings")); auto_ptr<string> strAutoPtr; strAutoPtr = strPtrRef; Test(strAutoPtr, "strAutoPtr", "auto_ptr& operator=(auto_ptr_ref m).../n"); cout << endl; // 其他函数 // T* get() const; (1) int* pAddr = new int(5); cout << "pAddr = " << pAddr << endl; auto_ptr<int> addr(pAddr); cout << "addr.get() = " << addr.get() << endl; cout << endl; // T& operator*() const; (2) cout << "*pAddr = " << *pAddr << endl; cout << "*addr.get() = " << *addr.get() << endl; cout << endl; // T* operator->() const; (3) Base* pBase = new Base(3.14159); auto_ptr<Base> spBase(pBase); cout << "pBase->m_pi = " << pBase->m_pi << endl; cout << "spBase->m_pi = " << spBase->m_pi << endl; cout << endl; // void reset(T* p = 0); (4) intPtr4.reset(new int(-1)); Test(intPtr4, "intPtr4", "void reset(T* p = 0).../n"); // T* release(); (5) intPtr4.release(); Test(intPtr4, "intPtr4", "T* release().../n"); cout << endl; return 0;}运行结果:
auto_ptr_ref
的源码这个辅助类的源码比较简单, 没有什么说的。前面也分析过了,这个辅助类其实是为了帮助 auto_ptr
完成右值引用传参而设计的。
auto_ptr
构造函数的源码其中, auto_ptr
的成员变量 _Ty *_Myptr
指向被它管理的对象。 (1) 从原始指针中获取控制权。注意,由源码可知, auto_ptr
并没有将原始指针的控制权剥夺(从实现来看, 也不能剥夺, 因为 Ptr
不是指针引用,无法更改原始指针的指向),原始指针仍然保有对其资源的控制权。但是,该资源的释放权实际上已经交给了 auto_ptr
对象。如:
(2) 从 auto_ptr
对象中夺取对资源的控制权。由源码可知, _Right
将不再保有对其资源的控制。注意,这里是左值引用参数,不能接收右值参数。 (3) 与 (2) 类似。是针对可转换为 _Ty*
类型的 _Other*
类型的构造函数。 (4) 这个构造函数的参数是 auto_ptr_ref<_Ty>
类型的。注意,它不是左值引用类型的参数,因此可以接收右值类型。这也是右值传参的必经之路。
auto_ptr
析构函数的源码auto_ptr
拷贝赋值函数的源码这里涉及到解决“自我赋值”(assignment to self)的问题,详情请参阅《Effective C++》 Item 11: Handle assignment to self in operator=。 (1) 将同类型的 _Right
管理的资源移交给 *this
。其中,reset(_Right.release())
先将 _Right
的资源以返回值的形式移交,然后设置给 *this
, 这样就防止了“自我赋值”的时候出现问题。(如果 *this
就是 _Right
, 那么执行完 _Right.release()
后, *this
管理的资源已经以返回值的形式移交出来作为参数,然后又 reset
给了自己)注意,这里是左值引用参数,不能接收右值参数。 (2) 与(1)类似。是针对可转换为 _Ty*
类型的 _Other*
类型的拷贝函数。 (3) 将一个 auto_ptr_ref
类型的变量赋值给 *this
, 实际上是将资源的控制权移交给 *this
。这同样是为了传右值参数而设计的。这个函数体内冗余的代码同样是为了防止 _Right._Ref
等于 _Myptr
的情况。
auto_ptr
隐式类型转换函数(1) auto_ptr
到 auto_ptr_ref
的隐式转换函数。 由源码可知,该隐式转换也会剥夺 *this
对资源的管理权。 这个转换虽然代码短小,但是能量巨大, 右值类型的 auto_ptr
作参数传递时,全靠这个转换函数来起到周转的作用。当然,现在新的C++标准有更好的方法来解决这个问题 —— 右值引用。 (2) 可转换的类型…
auto_ptr
其他函数(1) 获取 *this
所管理资源的指针, 这个没什么说的。 (2) 重载 operator*()
操作符,让 *this
有指针的行为。 (3) 重载 operator->()
操作符,让 *this
有指针的行为。 (4) 重新设置 *this
管理的资源, 当然在此之前要将 *this
管理的资源释放掉。类似于 operator=
的检查,如果_Ptr
指向的资源就是 *this
管理的资源,就忽略这个操作。否则会提前释放资源。 (5) *this
移交出管理权,并将资源的指针返回。因此需要先记录下资源的地址,然后将 *this
指向资源的指针置为空,最后返回资源的地址。
auto_ptr
用以 RAII(Resource Acquisition Is Initialization) 思想实现对资源的管理(详情可参考《Effective C++》Item 13: Use objects to manage resources)。但auto_ptr
属于该思想实现的早期版本,现在的标准库已经不推荐使用该工具了。但是,了解auto_ptr
的功能和实现还是有必要的,其一是,它相当于是其它更复杂智能指针的简化版本,源码简单,容易上手,对后面学习其它智能指针做铺垫; 其二是, 学习 auto_ptr
可以让那个我们对 RAII 思想有所领悟。
新闻热点
疑难解答