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


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

  • 相关阅读:
    Oracle 中 varchar2(N) 与 varchar2(N char) 的区别
    EXP-00008: 遇到 ORACLE 错误 1455
    服务器重装Windows Server2008 R2操作系统
    h5页面自定义主题色(vue)
    初窥vue3.0
    ElasticSearch学习笔记_1
    mysql索引的使用
    什么时候使用视图
    Latex使用手册记录
    最大熵模型理论及NLP应用总结
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4921352.html
Copyright © 2011-2022 走看看