C++的引用计数j控制智能指针——>Java的引用计数管理共享对象

以前学C/C++的时候,总是说指针是多么的难,多么的难管理。现在想想自己对指针的应用还只是初级的水平,还要继续坚持,学习下去。今天学习Java的时候,提到了一个引用计数的概念,用来管理共享对象,这样才知道什么时候去释放对象(当然Java有垃圾回收机制,但是针对的是有些时候需要我们手动去释放一些对象)。

C++指针成员的管理

对于指针成员的管理有下面三种方法:(摘自《C++primer Fourth Edition》)

1.指针成员采用常规指针型行为。这样的类具有指针的所有缺陷但无需特殊的复制控制。

2.类可以实现所谓的“智能指针”行为。指针所指向的对象时共享的,但类能够防止悬垂指针。

3.类采取取值型行为,指针所指向的对象时唯一的,由每个类对象独立管理。

带指针成员的简单类

示例代码如下:
#include<iostream>
#include<string>
using namespace std;

typedef int type;

class HasPtr {
public:
	HasPtr(type *p, type i) : ptr(p), val(i) {}
	
	//对指针成员的操作
	int *getPtr() const { return ptr; }
	void setPtr(int *p) { ptr = p; }
	
	//对普通成员的操作
	int getVal() const { return val; }
	void setVal(int i) { val = i; }

	//对指针所指向值的操作
	int getPtrVal() const { return *ptr; }
	void setPtrVal(int i) { *ptr = i; } 

private:
	type *ptr;
	type val;
};

int main() {
	int obj = 0;

	HasPtr ptr1(&obj, 1);	//ptr1.ptr = &obj, ptr1.val = 1
	HasPtr ptr2(ptr1);		//ptr2.ptr = &obj, ptr2.val = 1
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setVal(11);
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setPtrVal(22);
	cout << "ptr1.getPtrVal() = " << ptr1.getPtrVal() << endl;
	cout << "ptr2.getPtrVal() = " << ptr2.getPtrVal() << endl;
	cout << "obj = " << obj << endl;

	int *ip = new int(33);
	HasPtr ptr3(ip, 10);
	delete ip;
	ip = NULL;
	ptr3.setPtrVal(1);		//这里内存都释放了,但是编译器居然没有报错
	cout << "ptr3.getPtrVal() = " << ptr3.getPtrVal() << endl;

	return 0;
}


注意:虽然运行的时候没有任何不正常的情况,但是我在测试(IDE:VS2012)的时候发现还是有异常,每次运行到最后一行(return 0前一行)想得到对象ptr3指针成员所指向的值时,就会弹出一个窗口,每次到这句就是通不过。所以还是有一点提示的。弹出窗口如下:

上面的这个程序显示了一个简单的带有指针成员的类,包含了一些对普通成员和指针成员的操作。从上面的结果可以看出,对于普通成员val的操作没有什么奇怪,对于指针成员ptr的操作有一些问题了,由于没有自己的复制构造函数,所以用现有对象ptr1复制给对象ptr2,那么ptr1和ptr2两个对象的指针成员指向了同一个目标对象。所以对其中一个对象的指针成员的操作都会影响到另一个对象。最后一个Ptr3这种情况就更加危险了,开始让ip指向一块被分配的内存空间,然后让Ptr3的指针成员也指向那块内存空间,后面把这块内存空间释放了以后,Ptr3对象的指针成员所指向的那块内存也就不存在了,但是后面我们接着对那块内存进行操作,所以这就很危险了,而且这种错误总是很难发现的。

智能指针类

示例代码如下:
#include<iostream>
using namespace std;

typedef int type;

//计数类
class UseConPtr {
	friend class HasPtr;
	type *ip;
	size_t use;
	UseConPtr(int *p) : ip(p), use(1) {}
};

class HasPtr {
public:
	HasPtr(type *p, type i) : ptr(new UseConPtr(p)), val(i) {}
	HasPtr(const HasPtr &orig) : ptr(orig.ptr), val(orig.val) { ++ptr->use; }
	
	HasPtr& operator=(const HasPtr& rhs) {
		++rhs.ptr->use;
		if (--ptr->use == 0)
			delete ptr;
		ptr = rhs.ptr;
		val = rhs.val;
		return *this;
	}
	~HasPtr() { if (--ptr->use == 0) delete ptr; }
	
	//对指针成员的操作
	int *getPtr() const { return ptr->ip; }
	void setPtr(int *p) { ptr->ip = p; }
	
	//对普通成员的操作
	int getVal() const { return val; }
	void setVal(int i) { val = i; }

	//对指针所指向值的操作
	int getPtrVal() const { return *ptr->ip; }
	void setPtrVal(int i) { *ptr->ip = i; } 

	int getUse() { return ptr->use; }
private:
	UseConPtr *ptr;
	type val;
};

int main() {
	int obj = 0;

	HasPtr ptr1(&obj, 1);	//ptr1.ptr = &obj, ptr1.val = 1
	cout << "ptr1.getUse() = " << ptr1.getUse() << endl;
	HasPtr ptr2(ptr1);		//ptr2.ptr = &obj, ptr2.val = 1
	cout << "ptr2.getUse() = " << ptr2.getUse() << endl;
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setVal(11);
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setPtrVal(22);
	cout << "ptr1.getPtrVal() = " << ptr1.getPtrVal() << endl;
	cout << "ptr2.getPtrVal() = " << ptr2.getPtrVal() << endl;
	cout << "obj = " << obj << endl;

	int *ip = new int(33);
	HasPtr ptr3(ip, 10);
	cout << "ptr3.getUse() = " << ptr3.getUse() << endl;

	ptr3.setPtrVal(1);		
	cout << "ptr3.getPtrVal() = " << ptr3.getPtrVal() << endl;
	delete ip;
	ip = NULL;

	return 0;
}

这里通过引用计数来实现的智能指针,主要是新建一个计数类UseConPtr,这个计数类保存着指针和使用计数,比如新建一个HasPtr的对象,那么计数类就保存了一个指向它的指针和计数个数(就是第几次引用它了),然后没通过该对象拷贝出一个新的HasPtr的对象,就多了一个对象指向这个计数类对象,同时该计数类对象的use成员加1,当然如果每次解除一个对象同时use成员也要减1,最后HasPtr的析构函数要判断use是否等于0,如果再没有对象引用时,就把这个计数类的内存给释放了,这样就构成了一个智能指针类。

定义值型类

示例代码如下:
#include<iostream>
using namespace std;

typedef int type;

class HasPtr {
public:
	HasPtr(const type p, type i) : ptr(new int(p)), val(i) {}
	HasPtr(const HasPtr &orig) : ptr(new int(*orig.ptr)), val(orig.val) { }
	
	HasPtr& operator=(const HasPtr& rhs) {
		*ptr = *rhs.ptr;
		val = rhs.val;
		return *this;
	}
	~HasPtr() { delete ptr; }

	//对指针成员的操作
	int *getPtr() const { return ptr; }
	void setPtr(int *p) { ptr = p; }
	
	//对普通成员的操作
	int getVal() const { return val; }
	void setVal(int i) { val = i; }

	//对指针所指向值的操作
	int getPtrVal() const { return *ptr; }
	void setPtrVal(int i) { *ptr = i; } 

private:
	int *ptr;
	type val;
};

int main() {
	int obj = 0;

	HasPtr ptr1(obj, 1);	//*ptr1.ptr == 0, ptr1.val = 1
	HasPtr ptr2(ptr1);		//*ptr2.ptr == 0, ptr2.val = 1
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setVal(11);
	cout << "ptr1.getVal() = " << ptr1.getVal() << endl;
	cout << "ptr2.getVal() = " << ptr2.getVal() << endl;

	ptr1.setPtrVal(22);
	cout << "ptr1.getPtrVal() = " << ptr1.getPtrVal() << endl;
	cout << "obj = " << obj << endl;
	cout << "ptr2.getPtrVal() = " << ptr2.getPtrVal() << endl;
	cout << "obj = " << obj << endl;

	return 0;
}

测试结果:


值型类中的指针成员对待方式更像一个普通成员了,没构造一个对象时,就让指针成员指向一块新分配的内存,然后复制构造时也是让新对象的指针成员指向一块新分配的内存,不过是把旧对象指针成员所指向的值复制给新对象指针成员所指向的值,当然赋值操作符执行时,也是对指针成员所指值的赋值。当然在析构函数里,对相应的内存进行释放。

Java中引用计数

示例代码如下:
import java.util.*;

class Fruits {
	private static long counter = 0;
	private final long id = counter++;
	private Shared shared;
	
	public Fruits(Shared shared) {
		System.out.println("Fruits(shared) " + id);
		this.shared = shared;
		this.shared.addRef();
	}
	
	public void dispose() {
		System.out.println("Fruits.dispose() " + id);
		shared.dispose();
	}
	
	public String toString() { return "Fruits " + id; }
}

class Apple extends Fruits {
	public Apple(Shared shared) {
		super(shared);
		System.out.println("Apple(shared) ");
	}
	
	public void dispose() {
		System.out.println("Apple.dispose() ");
		super.dispose();
	}
	
	public String toString() { return "Apple " + super.toString(); }
}

class Orange extends Fruits {
	public Orange(Shared shared) {
		super(shared);
		System.out.println("Orange(shared) ");
	}
	
	public void dispose() {
		System.out.println("Orange.dispose() ");
		super.dispose();
	}
	
	public String toString() { return "Orange " + super.toString(); }
}

class Peach extends Fruits {
	public Peach(Shared shared) {
		super(shared);
		System.out.println("Peach(shared) ");
	}
	
	public void dispose() {
		System.out.println("Peach.dispose() ");
		super.dispose();
	}
	
	public String toString() { return "Peach " + super.toString(); }
}

class Shared {
	private int refcount = 0;
	private static long counter = 0;
	private final long id = counter++;
	
	public Shared() {
		System.out.println("Creating " + this);
	}
	
	public void addRef() { refcount++; }
	
	protected void dispose() {
		if (--refcount == 0)
			System.out.println("Disposing " + this);
	}
	
	public String toString() { return "Shared " + id; }
}

class RandomFruitsGenerator {
	private Random rand = new Random();
	private Shared shared = new Shared();
	Fruits next() {
		switch(rand.nextInt(3)) {
			default:
			case 0: return new Apple(shared);
			case 1: return new Orange(shared);
			case 2: return new Peach(shared);
		}
	}
}

public class FruitsTest {
	private static RandomFruitsGenerator gen = new RandomFruitsGenerator();
	public static void main(String [] args) {
		Fruits [] fa = new Fruits[6];
		
		//随机产生6个对象
		for (int i=0; i<fa.length; i++)
			fa[i] = gen.next();
		
		//所有对象依次释放内存
		for (Fruits ff : fa)
			ff.dispose();
	}
}


首先要说明一点的是,这个例子主要说明的是当确实遇到清理时,比如这里Fruits中调用dispose()方法进行清理。这里假设了一种情况,当Shared类的对象同时被好几个对象共享时,那么我们就要特别注意什么时候去dispose()这个Shared对象,否则就会出现,你dispose()了Shared对象,但是还有其他的对象还引用这这个已经dispose的对象,那么问题就来了。这个时候我们要怎么杜绝这种情况呢,就是引入一个引用计数,就是每增加一个对象引用时就让计数器countor加1,这个时候我们就知道到底有多少个对象共享了,然后在Shared对象的dispose方法中进行一个判断,如果引用计数为0的时候,我们就把Shared对象给dispose了。这样就不存在上面那种情况了。

PS:这些东西书上都有的,但是自知资质平平所以复习一下增加记忆及熟练程度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值