scoped_ptr analysis

本文深入探讨了move_only_type_for_cpp_03宏的使用场景与实现细节,包括如何避免拷贝构造和赋值运算符的调用,以及其在c++03版本中对move语义的支持。通过源代码分析,展示了如何将这一机制整合到scoped_ptr类中,并通过实例代码演示了其在不同操作下的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
一、概述:
	1.1	宏MOVE_ONLY_TYPE_FOR_CPP_03是针对c++03实现move语义用的。在c++11中已经实现了T&& var的rvalue,而
		为了兼容老编译器,没有用新语法。所以,需要手动实现move语义。不过,这个宏所提供的机制在新的c++
		中照样能工作。(好像效率没有rvalue高,会生出一些临时变量)
	1.2	将拷贝构造函数type(type&);和赋值运算符都设置成私有,防止拷贝发生。
		因为在语义上,本指针是个作用域指针,一般声明在栈上。假设有p1,p2; p1内含有对象obj,如果copy语义发
		生的话 p2(p1);或 p2=p1;那么p1,p2同时含有obj对象指针,若二者其中一个先出了作用域,那么它会把obj
		释放掉,导致另一个指针对象非法。
	1.3	同时,拷贝构造函数三种情况:
			a. 作为函数返回值
			b. 作为函数参数
			c. 用一个对象构造另一个同类对象
		在这些时刻,拷贝构造函数会被隐式地调用,会造成麻烦,所以拷贝构造函数被私有。而赋值operator用起来
		会让人误以为两个指针都有obj对象地址,给程序员造成不便(不直观嘛),所以也给它私有了。
	1.4	私有了以后,因为永远不会被调用,则也没有具体的实现。
	
	1.5	scoped_ptr是有作用域的对象,出了作用域,它就会析构,进而删除体内new出来的对象。
		那么scoped_ptr不能是new出来的,否则需要有人显式地delete它。
二、源代码:
	对源代码进行了改变,把那个展开到scoped_ptr中去了,并且添加了打印信息,方便分析。
*/
	 
	   
template <class C>
class scoped_ptr {
	// MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
private:  
	struct RValue : public scoped_ptr { 
		RValue(){
			cout<<"rvalue_type()"<<this<<endl;
		} 
		~RValue(){
			cout<<"~rvalue_type()"<<this<<endl;
		} 
		RValue(const RValue&){
			cout<<"rvalue_type(const rvalue_type&)"<<this<<endl;
		} 
		void operator=(const RValue&){
			cout<<"void operator=(const rvalue_type&)"<<this<<endl;
		} 
	}; 
	scoped_ptr(scoped_ptr&); 
	void operator=(scoped_ptr&); 
public: 
	operator RValue&(){
		cout<<"=======operator rvalue_type&() of ["<<this<<"]"<<endl; 
		return *reinterpret_cast<RValue*>(this); 
	} 
	scoped_ptr Pass(){
		cout<<"in the Pass() of ["<<this<<"]"<<endl;
		return scoped_ptr(*reinterpret_cast<RValue*>(this)); 
	}
private:

public: 
	typedef C element_type; 
	explicit scoped_ptr(C* p = NULL) : ptr_(p) {
		cout<<"explicit scoped_ptr(C* p ="<< p<<") : ptr_(p) current:["<<this<<"]"<<endl; 
	}
	template <typename U>
	scoped_ptr(scoped_ptr<U> other){
		cout<<"scoped_ptr(scoped_ptr<U> other="<<&other<<"):ptr_(other.release())current:["<<this<<"]"<<endl;
		ptr_=(other.release());
	}  
	scoped_ptr(RValue& other)   
	{
		cout<<" scoped_ptr(RValue& other="<<&other<<") current:["<<this<<"]"<<endl;
		ptr_=(reinterpret_cast<scoped_ptr&>(other).release()) ;
	} 
	~scoped_ptr() {
		cout<<"  ~scoped_ptr() current:["<<this<<"]"<<endl;
		enum { type_must_be_complete = sizeof(C) };
		delete ptr_;
	}
	template <typename U>
	scoped_ptr& operator=(scoped_ptr<U> rhs) {
		cout<<"scoped_ptr& operator=(scoped_ptr<U> ="<<&rhs<<") current:["<<this<<"]"<<endl;
		reset(rhs.release());
		return *this;
	}
	scoped_ptr& operator=(RValue& rhs) {
		cout<<"scoped_ptr& operator=(RValue& rhs="<<&rhs<<") current:["<<this<<"]"<<endl;
		swap(rhs);
		return *this;
	}
	void reset(C* p = NULL) {cout<<"void reset(C* p = "<<p<<") current:["<<this<<"]"<<endl;
		if (p != ptr_) {
			enum { type_must_be_complete = sizeof(C) };
			delete ptr_;
			ptr_ = p;
		}
	}
	C& operator*() const {cout<<"C& operator*() const current:["<<this<<"]"<<endl;
		assert(ptr_ != NULL);
		return *ptr_;
	}
	C* operator->() const  {cout<<"C* operator->() const current:["<<this<<"]"<<endl;
		assert(ptr_ != NULL);
		return ptr_;
	}
	C* get() const { 
		cout<<"C* get() const current:["<<this<<"]"<<endl;
		return ptr_; 
	}
	bool operator==(C* p) const { return ptr_ == p; }
	bool operator!=(C* p) const { return ptr_ != p; }

	void swap(scoped_ptr& p2) {
		cout<<"void swap(scoped_ptr& p2="<<p2<<")  current:["<<this<<"]"<<endl;
		C* tmp = ptr_;
		ptr_ = p2.ptr_;
		p2.ptr_ = tmp;
	}
	C* release()   {
		cout<<"C* release()  current:["<<this<<"]"<<endl;
		C* retVal = ptr_;
		ptr_ = NULL;
		return retVal;
	}
	template <typename PassAsType>
	scoped_ptr<PassAsType> PassAs() {
		cout<<"scoped_ptr<PassAsType> PassAs() current:["<<this<<"]"<<endl;
		return scoped_ptr<PassAsType>(release());
	}
private:
	C* ptr_;
	template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
	template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
};
	
#include "iostream"
#include "myheader.h"
using namespace std;

class node{};
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

	// TODO: 在此处为应用程序的行为编写代码。
	cout<<"--------------constructor -------------------"<<endl<<endl;
	scoped_ptr<node> p11(new node());
	scoped_ptr<node> p22=p11.Pass();//这里不是‘=’的赋值,而是构造
	//p22=p11;//这样是错误的,因为operator =是private
	cout<<"p11   "<<&p11<<"    p22   "<<&p22<<endl<<endl;		 

	cout<<"--------------operator = -------------------"<<endl;			
	scoped_ptr<node> p1(new node());
	scoped_ptr<node> p2;				
	cout<<"p1   "<<&p1<<"    p2   "<<&p2<<endl;
	p2=p1.Pass();//这里是赋值操作operator =

	system("pause");
	return 0;
}
//----------------------执行结果及分析------------------------------------
--------------constructor -------------------
//p11构造
explicit scoped_ptr(C* p =0058D680):ptr_(p)current:[0025F9F4]
//p11.pass()
in the Pass() of [0025F9F4]
//tmp1.scoped_ptr(RValue& other)
 scoped_ptr(RValue& other=0025F9F4) current:[0025F79C]
//p11.release()释放持有的资源给tmp1
C* release()  current:[0025F9F4]
//本来pass中要调用tmp2.scoped_ptr(tmp1)[拷贝构造],但
//这个函数是私有的。而编译器又发现有一个scoped_ptr(RValue& other)
//函数可用来构造,但它要求参数是RValue的,进一步地,编译器发现
//tmp1有个operator可实现此转化:operator rvalue_type&(),所以就
//先调用了tmp1的operator rvalue_type&()来将tmp1从scoped_ptr转
//到RValue类型,转化是安全的,因它们内部都只有一个指针数据。
=======operator rvalue_type&() of [0025F79C]
//调用完这个函数tmp2就构造好了
 scoped_ptr(RValue& other=0025F79C) current:[0025F8C8]
//tmp1用完了,它将其内部资源释放给tmp2、析构自己
C* release()  current:[0025F79C]
  ~scoped_ptr() current:[0025F79C]
//同样的过程,p22.拷贝构造(tmp2)也是私有的,重复上述过程
=======operator rvalue_type&() of [0025F8C8]
 scoped_ptr(RValue& other=0025F8C8) current:[0025F9E8]
C* release()  current:[0025F8C8]
  ~scoped_ptr() current:[0025F8C8]
p11   0025F9F4    p22   0025F9E8
/*
	另外通过汇编观察,主要是跟踪ecx和push的参数变化情况总结:
	进pass前,main中有一个局部变量名为tmp2,传给pass.
	pass内部也有一个局部变量名为tmp1
	p11初始化tmp1,tmp1初始化tmp2,tmp2初始化p22
	相当于tmp2是pass外面的一个临时变量,tmp1是pass里面的一个临时变量
*/
--------------operator = -------------------
explicit scoped_ptr(C* p =0058D710) : ptr_(p) current:[0025F9DC]
explicit scoped_ptr(C* p =00000000) : ptr_(p) current:[0025F9D0]
p1   0025F9DC    p2   0025F9D0
in the Pass() of [0025F9DC]
 scoped_ptr(RValue& other=0025F9DC) current:[0025F79C]
C* release()  current:[0025F9DC]
=======operator rvalue_type&() of [0025F79C]
 scoped_ptr(RValue& other=0025F79C) current:[0025F8F8]
C* release()  current:[0025F79C]
  ~scoped_ptr() current:[0025F79C]
=======operator rvalue_type&() of [0025F8F8]
 scoped_ptr(RValue& other=0025F8F8) current:[0025F890]
C* release()  current:[0025F8F8]
scoped_ptr& operator=(scoped_ptr<U> =0025F890)  current:[0025F9D0]
C* release()  current:[0025F890]
void reset(C* p = 0058D710) current:[0025F9D0]
  ~scoped_ptr() current:[0025F890]
  ~scoped_ptr() current:[0025F8F8]
请按任意键继续. . .
  
/*
	第二部分因为有个赋值操作,所以多了一次临时变量。在scoped_ptr模板中
	还有两个函数:
	template <typename U> scoped_ptr(scoped_ptr<U> other)
	template <typename U> scoped_ptr& operator=(scoped_ptr<U> rhs) 
	这两个函数原理一样:
	在c++03中,临时变量属于右值。它的性质与const &类似,即不能被改变。
	当pass()返回一个临时变量时(实际上是把有名变量变成无名临时的了),编译
	器要找一个拷贝构造或者类似这样的、带常引用参数的operator =:
	scoped_ptr& operator=(const scoped_ptr& rhs) 
	但,现实情况是scoped_ptr没有这样的函数,那么编译器接着找合适的函数,
	于是发现了上面两个函数可用。这就是它们存在的原因。
	同时,如果p2=p1的话,那么与上面相反,p1是有名变量,是个lvalue,不是
	const &型的,当然要匹配非-常引用参数的operator =或copy(T&),因为它们
	是私有的,所以就会报错。
*/ 
/*
三、其它:
		返回值优化(RVO),有了它,正常情况函数按值返回对象,里面来个临时变量,
	外面那个对象p2的拷贝构造会被调用一次,就完事了。但由于我们要实现move语
	义,而且是c++03版的语法,所以就有好几个临时变量。
	
*/
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值