现在的位置: 首页 > 综合 > 正文

C++中智能指针的实现

2013年09月29日 ⁄ 综合 ⁄ 共 3243字 ⁄ 字号 评论关闭

C++学习中遇到的问题,记录之。使用的教材: 《C++ primer》。

智能指针?what? how?
What ‘s the smartpointer and how to impletment it?

什么是智能指针?
通过引用计数,自动管理动态分配的内存的生存期,避免内存泄漏或悬垂指针的方法。
应用场景:
如果类的一个成员是指向动态分配的内存块的指针,在做复制控制的时候,我们有两种方法:
1、复制指针指向的内存块,为了避免内存泄漏,会在类的析构函数里释放内存。这样为造成内存空间的浪费,copy操作也会带来不必要的开销。
2、只复制指针。但引入了新的问题:当通过一个副本对象的指针成员释放了这个内存块之后,其它指针就成了所谓的悬垂指针。

这里引入智能指针,来解决2 中带来的问题。

如何实现智能指针?
为了解决这个问题,我们在方法2里,加入引用计数的方法,来管理这个对象的内存释放。
假设我们这里需要管理的是一个int型指针ip指向的内存。如下图:


通过U_ptr来将指针和一个引用计数绑定在一起,然后类HasPtr通过一个指向U_ptr的指针成员来间接访问 ip 指向的内存。
(这里应该限制,一个内存块只对应一个U_ptr的对象)
U_ptr的引用计数 use 初始化为1.
所有对类HasPtr的对象的复制控制操作,都会修改U_ptr的引用计数。
每次对这个HasPtr对象的copy,都不copy 指针 ip 指向的内存块,但对U_ptr的use加一。
每一个对象的析构函数中,都判断use减一后是否为0,如果是,则销毁U_ptr对象绑定的那个内存块。

//C++ primer plus ...... chapter 8  page253

#include <iostream>
#include <string>

#include <new>
using namespace std;

class U_Ptr{
private:
	friend class HasPtr;
	int *ip;
	size_t use;
	U_Ptr(int *p):ip(p),use(1){ }
	~U_Ptr() {
		cout << "U_Ptr destructor...free mem ip = " << ip << endl;
		delete ip; 
	}
};

class HasPtr{
public:
	HasPtr(const string &str, int *p):name(str),ptr(new U_Ptr(p)) {
		cout << name << "\tHasPtr do normal construct" << endl;
	}
	HasPtr(const string &str, const HasPtr &orig):name(str),ptr(orig.ptr) {
		cout << name << "\tHasPtr do copy construct" << endl;
		++ptr->use; 
	}

	//some friend operators
	friend std::ostream& operator<<(std::ostream& , const HasPtr &);

	HasPtr& operator=(const HasPtr &orig)
	{
		cout << name << "\tHasPtr do assignment from "<< orig.name << endl;
		if(orig.ptr == this->ptr){
			cout << "They are the same one, do nothing" << endl;
			return *this;
		}

		++orig.ptr->use; 
		if(--ptr->use == 0){
			cout << name << "\tdelete ptr "<< endl;
			delete ptr;
		}else {
			cout << name << "\tptr usecnt = " << ptr->use << endl;
		}
		ptr = orig.ptr;
		return *this;
	}

	~HasPtr()
	{
		--ptr->use;
		cout << name << "\tHasPtr destructor ... ptr usecnt = " << ptr->use << endl;
		if( ptr->use == 0 ){
			delete ptr; //here call U_Ptr's destructor
		}
	}

	//some api
	int * get_ptr() const {return ptr->ip ;}
	int get_ptr_val() const { return *(ptr->ip) ;}

	void set_ptr(int *p) { ptr->ip = p ;}
	void set_ptr_val(int i) { *ptr->ip = i ;}

private:
	string name;
	U_Ptr *ptr;
};

std::ostream& operator<<(std::ostream& os , const HasPtr &hp)
{
	os << hp.name <<"\tval = "<< hp.get_ptr_val() << "\taddr = "<< hp.get_ptr();
	return os;
}

//overloaded the operator "<<".
int main(int argc, char const *argv[])
{
	int *a = new int(123);
	cout << endl << "normal constructor" << endl;
	HasPtr cp1("cp1", a);
	cout << cp1 << endl;

	cout << endl << "copy constructor" << endl;
	HasPtr cp2("cp2",cp1);
	cp2.set_ptr_val(199);
	cout << cp2 << endl;
	
	cout << endl << "assignment operator " << endl;
	cp2 = cp1 ;
	cout << cp2 << endl;
	
	cout << endl << "more HasPtr to a" << endl;
	HasPtr cp3("cp3" , a);
	HasPtr cp4("cp4" , a);
	cout << cp3 << endl;
	cout << cp4 << endl;

	if(a!=NULL)
		cout << "a is still keep the mem, addr = " << *a << endl;
	else
		cout << "a has been free mem" << endl;

	cout << endl << ".................exit from main" << endl;
	//delete(a); //do not need free mem here
	return 0;
}

在linux下,使用g++编译,运行结果:

normal constructor
cp1	HasPtr do normal construct
cp1	val = 123	addr = 0x9f0d008

copy constructor
cp2	HasPtr do copy construct
cp2	val = 199	addr = 0x9f0d008

assignment operator 
cp2	HasPtr do assignment from cp1
They are the same, do nothing
cp2	val = 199	addr = 0x9f0d008

more HasPtr to a
cp3	HasPtr do normal construct
cp4	HasPtr do normal construct
cp3	val = 199	addr = 0x9f0d008
cp4	val = 199	addr = 0x9f0d008
a is still keep the mem, addr = 199

.................exit from main
cp4	HasPtr destructor ... ptr usecnt = 0
U_Ptr destructor...free mem ip = 0x9f0d008
cp3	HasPtr destructor ... ptr usecnt = 0
U_Ptr destructor...free mem ip = 0x9f0d008
cp2	HasPtr destructor ... ptr usecnt = 1
cp1	HasPtr destructor ... ptr usecnt = 0
U_Ptr destructor...free mem ip = 0x9f0d008

【上篇】
【下篇】

抱歉!评论已关闭.