【more effective c++读书笔记】【第5章】技术(6)——Proxy classes(代理类)

“用来代表其他对象”的对象被称为proxy objects,用来表现proxy objects者,称为proxy classes(代理类)。

一、实现二维数组

//Array2D.h
#ifndef ARRAY2D_H
#define ARRAY2D_H
//代理类
template<typename T>
class Array1D{
public:
	Array1D(int d) :dim(d),data1d(new T[dim]){}//构造函数,T必须有默认构造函数
	Array1D(const Array1D& rhs):dim(rhs.dim),data1d(new T[dim]){//深拷贝构造函数
		for (int i = 0; i < dim; ++i)
			data1d[i] = rhs.data1d[i];
	}
	~Array1D(){ delete[] data1d; }//析构函数

	T& operator[](int index){ return data1d[index]; }//重载[]运算符,非const版本
	const T& operator[](int index) const { return data1d[index]; }//重载[]运算符,const版本
	int getLength(){ return dim; }//返回一维数组长度
private:
	int dim;
	T* data1d;
};

template<typename T>
class Array2D{
public:
	Array2D(int d1, int d2):dim1(d1),dim2(d2){ //构造函数,T必须有默认构造函数
		void* raw = ::operator new[](dim2*sizeof(Array1D<T>(dim1)));//分配原始内存
		data2d = static_cast<Array1D<T>*>(raw);//data2d指向原始内存,并使这块内存被当做Array1D<T>数组
		for (int i = 0; i < dim2; ++i)//利用placement new构造内存中的Array1D<T>对象
			new (data2d + i) Array1D<T>(dim1);
	}
	Array2D(const Array2D& rhs):dim1(rhs.dim1),dim2(rhs.dim2){//深拷贝构造函数
		void* raw = ::operator new[](dim2*sizeof(Array1D<T>(dim1)));
		data2d = static_cast<Array1D<T>*>(raw);
		for (int i = 0; i < dim2; ++i)//利用placement new拷贝构造内存中的Array1D<T>对象
			new (data2d + i) Array1D<T>(rhs.data2d[i]);
	}
	~Array2D(){//析构函数,没有用new来创建data2d数组,就不能直接用delete[]来删除data2d 
		for (int i = 0; i<dim2; ++i)
			data2d[i].~Array1D<T>(); //显式调用析构函数销毁各个对象  
		::operator delete[](static_cast<void*>(data2d)); //释放内存  
	}
	Array1D<T>& operator[](int index){ return data2d[index]; }//重载[]运算符,非const版本
	const Array1D<T>& operator[](int index) const { return data2d[index]; }//重载[]运算符,const版本
	int getLength1(){ return dim1; }//返回数组第一维长度
	int getLength2(){ return dim2; }//返回数组第二维长度
private:
	int dim1;
	int dim2;
	Array1D<T>* data2d;
};

#endif
//main.cpp
#include"Array2D.h"
#include<iostream>
using namespace std;

int main(){
	Array2D<int> data(10, 20);
	int count = 0;
	for (int i = 0; i < 10; ++i){
		for (int j = 0; j < 20; ++j){
			data[i][j] = count++;
		}
	}
	for (int i = 0; i < 10; ++i){
		for (int j = 0; j < 20; ++j){
			cout << data[i][j] << ' ';
		}
	}
	system("pause");
	return 0;
}

二、区分operator[]的读写动作

operator[]可以在两种不同情境下被调用:用来读取一个字符,或是用来写一个字符。读取动作是右值运用,写动作是左值运用。写一个引用计数对象,可能需要复制一份完整的数据结构,而读取则只是简单返回一个值。

//RCObject.h
#ifndef RCOBJECT_H
#define RCOBJECT
//引用计数基类
class RCObject{
public:
	void addReference();//增加引用计数
	void removeReference();//减少引用计数,如果变为0,销毁对象
	void markUnshareable();//将追踪其值是否可共享的成员设为false
	bool isShareable() const;//判断其值是否可共享
	bool isShared() const;//判断其值是否正在被共享
	int getRefCount();//返回引用计数
protected:
	RCObject();//构造函数
	RCObject(const RCObject& rhs);//拷贝构造函数
	RCObject& operator=(const RCObject& rhs);//拷贝赋值运算符
	virtual ~RCObject() = 0;//析构函数
private:
	int refCount;//保存引用计数
	bool shareable;//保存其值是否可共享的状态
};
//构造函数,这里refCount设为0,让对象创建者自行或将refCoun设为1
RCObject::RCObject(void) :refCount(0), shareable(true){}
//拷贝构造函数,总是将refCount设为0,因为正在产生一个新对象,只被创建者引用
RCObject::RCObject(const RCObject&) : refCount(0), shareable(true){}
//拷贝赋值运算符,这里只返回*this,因为左右两方RCObject对象的外围对象个数不受影响
RCObject& RCObject::operator=(const RCObject& rhs){
	return *this;
}
//析构函数
RCObject::~RCObject(){}
//增加引用计数
void RCObject::addReference(){
	++refCount;
}
//减少引用计数,如果变为0,销毁对象
void RCObject::removeReference(){
	if (--refCount == 0)
		delete this;
}
//将追踪其值是否可共享的成员设为false
void RCObject::markUnshareable(){
	shareable = false;
}
//判断其值是否可共享
bool RCObject::isShareable() const{
	return shareable;
}
//判断其值是否正在被共享
bool RCObject::isShared() const{
	return refCount>1;
}
//返回引用计数
int RCObject::getRefCount(){
	return refCount;
}

#endif
//RCPtr.h
#ifndef RCPTR_H
#define RCPTR_H
//智能指针模板类,用来自动执行引用计数类成员的操控动作
template<typename T>
class RCPtr{
public:
	RCPtr(T* realPtr = 0);//构造函数
	RCPtr(const RCPtr& rhs);//拷贝构造函数
	~RCPtr();//析构函数
	RCPtr& operator=(const RCPtr& rhs);//拷贝赋值运算符
	T* operator->() const;//重载->运算符
	T& operator*() const;//重载*运算符
private:
	T* pointee;
	void init();//共同的初始化操作
};
//共同的初始化操作
template<typename T>
void RCPtr<T>::init(){
	if (pointee == 0) return;
	if (pointee->isShareable() == false) {
		pointee = new T(*pointee);
	}
	pointee->addReference();
}
//构造函数
template<typename T>
RCPtr<T>::RCPtr(T* realPtr) :pointee(realPtr){
	init();
}
//拷贝构造函数
template<typename T>
RCPtr<T>::RCPtr(const RCPtr& rhs) : pointee(rhs.pointee){
	init();
}
//析构函数
template<typename T>
RCPtr<T>::~RCPtr(){
	if (pointee)
		pointee->removeReference();
}
//拷贝赋值运算符
template<typename T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs){
	if (pointee != rhs.pointee) {
		if (pointee)
			pointee->removeReference();
		pointee = rhs.pointee;
		init();
	}
	return *this;
}
//重载->运算符
template<typename T>
T* RCPtr<T>::operator->() const { return pointee; }
//重载*运算符
template<typename T>
T& RCPtr<T>::operator*() const { return *pointee; }

#endif
//String.h
#ifndef STRING_H
#define STRING_H

#define _CRT_SECURE_NO_WARNINGS
#include"RCObject.h"
#include"RCPtr.h"
#include<iostream>

class String {
public:
	//代理类,为了区分左值和右值
	class CharProxy{
	public:
		CharProxy(String& str, int index);//构造函数
		CharProxy& operator=(const CharProxy& rhs);//左值运用
		CharProxy& operator=(char c);
		operator char() const;//右值运用
		char* operator&();//重载&运算符,非const版本
		const char* operator&() const;//重载&运算符,const版本
	private:
		String& theString;//这个proxy所附属的字符串
		int charIndex;//这个proxy所代表的字符串字符
	};
	String(const char *value = "");//构造函数
	const CharProxy operator[](int index) const;//重载[]运算符,针对const Strings,注意返回CharProxy对象
	CharProxy operator[](int index);//重载[]运算符,针对non-const Strings,注意返回CharProxy对象
	int getRefCount();//返回引用计数
	friend std::istream& operator>>(std::istream& is, const String& str);//重载>>运算符
	friend std::ostream& operator<<(std::ostream& os, const String& str);//重载<<运算符
private:
	struct StringValue : public RCObject {//继承自引用计数基类
		char *data;
		StringValue(const char *initValue);//构造函数
		StringValue(const StringValue& rhs);//拷贝赋值运算符
		void init(const char *initValue);
		~StringValue();//析构函数
	};
	RCPtr<StringValue> value;//智能指针对象
};
//String::CharProxy实现代码
//构造函数
String::CharProxy::CharProxy(String& str, int index) :theString(str), charIndex(index){}
//左值运用
String::CharProxy& String::CharProxy::operator=(const CharProxy& rhs){
	//如果本字符串与其他String对象共享一个实值,将实值复制一份,供本字符串单独使用
	if (theString.value->isShared())
		theString.value = new StringValue(theString.value->data);
	//赋值动作
	theString.value->data[charIndex] = rhs.theString.value->data[rhs.charIndex];
	return *this;
}
String::CharProxy& String::CharProxy::operator=(char c){
	//如果本字符串与其他String对象共享一个实值,将实值复制一份,供本字符串单独使用
	if (theString.value->isShared())
		theString.value = new StringValue(theString.value->data);
	//赋值动作
	theString.value->data[charIndex] = c;
	return *this;
}
//右值运用
String::CharProxy::operator char() const{
	return theString.value->data[charIndex];
}
//重载&运算符,非const版本
char* String::CharProxy::operator&(){
	if (theString.value->isShared())
		theString.value = new StringValue(theString.value->data);
	theString.value->markUnshareable();
	return &(theString.value->data[charIndex]);
}
//重载&运算符,const版本
const char* String::CharProxy::operator&() const{
	return &(theString.value->data[charIndex]);
}
//String::StringValue实现代码
void String::StringValue::init(const char *initValue){
	data = new char[strlen(initValue) + 1];
	strcpy(data, initValue);
}
//StringValue类的构造函数
String::StringValue::StringValue(const char *initValue){
	init(initValue);
}
//StringValue类的拷贝赋值运算符
String::StringValue::StringValue(const StringValue& rhs){
	init(rhs.data);
}
//StringValue类的析构函数
String::StringValue::~StringValue(){
	delete[] data;
}
//String实现代码
//String类的构造函数
String::String(const char *initValue)
: value(new StringValue(initValue)) {}
//重载[]运算符,针对const Strings,注意返回CharProxy对象
const String::CharProxy String::operator[](int index) const{
	return CharProxy(const_cast<String&>(*this), index);
}
//重载[]运算符,针对non-const Strings,注意返回CharProxy对象
String::CharProxy String::operator[](int index){
	return CharProxy(*this, index);
}
//返回引用计数
int String::getRefCount(){
	return value->getRefCount();
}
//重载>>运算符
std::istream& operator>>(std::istream& is, const String& str){
	is >> str.value->data;
	return is;
}
//重载<<运算符
std::ostream& operator<<(std::ostream& os, const String& str){
	os << str.value->data;
	return os;
}

#endif
//main.cpp
#include"String.h"
#include<iostream>
using namespace std;

int main(){
	String str1("hello world");
	String str2 = str1;//调用拷贝构造函数
	String str3;//调用默认构造函数
	str3 = str2;//调用拷贝赋值运算符
	
	cout << str1[4] << endl; // 'o' 右值运用

	str2[0] = 'H';//左值运用
	cout << str1 << endl; //"hello world"
	cout << str2 << endl;//"Hello world"
	cout << str3 << endl;//"hello world" 
	cout << "str1的引用计数是:" << str1.getRefCount() << endl; // 2
	cout << "str2的引用计数是:" << str2.getRefCount() << endl; // 1
	cout << "str3的引用计数是:" << str3.getRefCount() << endl; // 2

	str1[3] = str2[8];//左值运用
	cout << str1 << endl; //"helro world"
	cout << str2 << endl;//"Hello world"
	cout << str3 << endl;//"hello world" 
	cout << "str1的引用计数是:" << str1.getRefCount() << endl; // 1
	cout << "str2的引用计数是:" << str2.getRefCount() << endl; // 1
	cout << "str3的引用计数是:" << str3.getRefCount() << endl; // 1

	char* p = &str1[0];//如果CharProxy类不重载&运算符,出现错误,
	//对proxy取址获得的指针类型和对真实对象取址所获得的指针类型不同

	system("pause");
	return 0;
}

代理类的限制:

a、对proxy取址获得的指针类型和对真实对象取址所获得的指针类型不同;

b、通过proxies调用真实对象的成员函数会失败;

c、用户不能将它们传递给接受非常量引用对象的函数,否则会出错;

d、proxies难以完全取代真正对象的最后一个原因在于隐式转换。

总结:

代理类可以完成某些十分困难或几乎不可能完成的行为。多维数组是其中之一,左值/右值的区分是其中之二,压抑隐式类型转换是其中之三。

proxy类也有缺点。作为函数返回值,代理对象是临时对象,必须被构造和析构。代理对象的存在也增加了软件的复杂度。最后,当类的身份从与真实对象合作转移到与替身对象合作,往往会造成类语义的改变,因为代理对象所展现的行为常常和真正对象的行为有些隐微差异。


版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/ruan875417/p/4921352.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值