C++代理类,句柄(智能指针)_C++沉思录笔记
代理类
首先定义三个类:
class Animal{ public: virtual void getName()=0; virtual void clone()=0; }; class Cat:public Animal{ public: void getName(){cout<<"this is Cat"<<endl;} Animal* clone(){return new Cat;} }; class Dog:public Animal{ public: void getName(){cout<<"this is Dog"<<endl;} Animal* clone(){return new Dog;} };
一个 Animal 基类,两个 Animal 的派生类 Dog,Cat,好了做完这个后,现在想开设一个动物园 Zoo,容纳 50 只动物(即这些猫猫狗狗).于并用一个特定且唯一的 Animal_id 来指向 Zoo 里面的一只动物:
Animal* Zoo[50]; int Animal_id = 0; /* ... */ Zoo[Animal_id++] = new Car;
在开设动物园中的当我们又一只动物死掉了以后,便需要从 Zoo 中还存活的一只动物进行克隆去取代它的位置:
delete Zoo[DeadAnimal_id]; Zoo[DeadAnimal_id] = Zoo[CloneAnimal_id]->clone();
突然发现开这样一个动物园太累了,于是我们可以请一些员工来照看这些动物,这些员工每一个对应一只动物,然后我们就可以把克隆动物,处理死掉的动物:
class AgentManageAnimal{ public: AgentManageAnimal():an(0){} AgentManageAnimal(Animal& argAn):an(argAn.clone()){} AgentManageAnimal(AgentManageAnimal& argAn):an(argAn.an?argAn.an->clone():0){} ~AgentManageAnimal(){delete an;} AgentManageAnimal operator =(const AgentManageAnimal argAn){ if(this != &argAn) { delete an; an = (argAn.an?argAn.an->clone():0); } return *this; } void getName(){ if(an==0) cout<<"there is no Animal!"; else an->getName(); } public: Animal* an; };
最后我们看看我们的动物园在请了这些代理照看动物的员后是怎么运作的:
AgentManageAnimal Zoo[50]; int Animal_id = 0; Cat domi; //AgentManageAnimal(domi) 代表的是创建一个匿名对象 Zoo[Animal_id++] = AgentManageAnimal(domi);
这样我们便可以便捷的管理动物园了.
整理了下,代理类应该具备的哪些内容.以下表列出:
句柄类
上面介绍了一种叫做代理的类,用于让我们在同一个容器中存储类型不同但是相互关联的对象,这种方法需要为每个对象创建一个代理,并要将这些代理存储在容器中.创建代理将会复制所代理的对象,就像复制代理一样,那么当一个对象非常大或者是一种不能轻易复制的资源的时候,这个代理便无法满足我们的需求了.以下要介绍的句柄类,便可以轻松解决这个问题,并且它允许我们在保持代理的多态行为的同时避免不必要的复制.
句柄一般可以设计为两种样式,一种是数据和引用计数器粘合在一起存放,一种是数据和引用计数器分开存放.如下图:
如何设计句柄类?
数据和引用计数器存放在一起
首先我们定义一个类:
class Cat{ public: Cat(){} Cat(const Cat& argCat):name(argCat.name){} Cat(char* argName){name=argName;} void getName(){cout<<"this is "<<name<<endl;} void changeName(char* newName){name=newName;} private: string name; };
一个 Cat 类,现在我们有一个宠物猫店,里面有许多宠物猫而且我们又很多管理员来管理这些猫.然后每个猫脖子上面都挂着一个牌子(这个牌子只适用套在猫的脖子上),上面有一个条形码记录这对应猫和管理这只猫的管理员数量:
class IdCard{ public: friend class Handle; int num; Cat cat; IdCard():num(1){} IdCard(const Cat& argCat):cat(argCat),num(1){} IdCard(char* argName):cat(argName),num(1){} };
因为出售猫比较赚钱.所以呢我们里面有很雇来的管理员.且一个管理员只照看一只猫,为了让猫时刻都有人照顾所以一只猫可以用多个管理员来照顾,每个管理员手上都有一个与猫脖子上的牌子对应识别器.并且我们在宠物店里面规定,管理员可以给猫换名字,但是管理员在这么做前要先用识别器查看下现在有多少人在管理这只猫,若只要他一人,那么他可以直接给这个猫换名字,但是若管理这只猫的管理员不止他一人,那么他就要去那一只新的猫,然后在给这个猫取名字.这个管理器如下:
class Handle{ public: Handle():card(new IdCard){} Handle(char* argName):card(new IdCard(argName)){} Handle(const Handle& argHandle):card(argHandle.card){card->num++;} Handle(const IdCard& argIdCard):card(new IdCard(argIdCard)){} ~Handle(){ if(--card->num == 0) delete card; }; Handle& operator=(Handle &h){ h.card->num ++; if(--card->num == 0) delete card; card = h.card; return *this; } void getName(){card->cat.getName();} void changeName(char* newName){ if(card->num != 1) { --card->num; card = new IdCard(newName); } card->cat.changeName(newName); } private: IdCard* card; };
整理了下,句柄类和计数器类应该具备的哪些内容.以下表列出:
数据和引用计数器不存放在一起
在开了一段宠物店的后,我们的宠物店中不仅仅有猫,并且我们也引进了狗.我们来更新下宠物类:
class Animal{ public: virtual void getName()=0; virtual void changeName(char* newName)=0; virtual Animal* clone()=0; }; class Cat:public Animal{ public: Cat(){} Cat(const Cat& argCat):name(argCat.name){} Cat(char* argName){name=argName;} void getName(){cout<<"the cat is "<<name<<endl;} void changeName(char* newName){name=newName;} Animal* clone(){return new Cat(*this);} private: string name; }; class Dog:public Animal{ public: Dog(){} Dog(const Dog& argDog):name(argDog.name){} Dog(char* argName){name=argName;} void getName(){cout<<"The dog is "<<name<<endl;} void changeName(char* newName){name=newName;} Animal* clone(){return new Dog(*this);} private: string name; };
这时猫脖子上的牌子和识别器就有些不适用了.所以我们更新了了下设备.我们引进了一个可以挂在脖子上的牌子,这个牌子的链子,长度可伸缩.以便可以套在大型犬的脖子上.这个牌子将不再记录着牌子对应的动物是哪只动物了,只记录着这只动物有几个管理员共同照看着:
class IdCard{ private: int* num; public: IdCard():num(new int(1)){} IdCard(const IdCard& argCard):num(argCard.num){*num++;} ~IdCard(){if(only())delete num;} bool only(){return *num == 1;} bool reattach(const IdCard& argCard){ ++*argCard.num; if(only()){ delete num; num = argCard.num; return true; } *num--; num = argCard.num; return false; } bool makeOnly(){ if(only()) return false; *num--; num = new int(1); return true; } };
与此牌子和对应的是一个新的识别器,当然需要改名字时候还是一样,管理员在这么做前要先用识别器查看下现在有多少人在管理这只动物,若只要他一人,那么他可以直接给这个动物换名字,但是若管理这只动物的管理员不止他一人,那么他就要去找一只品种一样动物,然后在给这个动物取名字:
class Handle{ public: Handle(Animal* argAnimal):animal(argAnimal->Clone()){} Handle(const Handle& argHandle):animal(argHandle.animal),card(argHandle.card){} ~Handle(){ if(card.only()) delete animal; }; Handle& operator=(Handle &h){ if(card.reatach(h.card)) delete animal; animal = h.animal; return *this; } void getName(){ animal->getName();} void changeName(char* newName){ if(card.makeOnly()) animal = animal->Clone(); animal->changeName(newName); } private: Animal* animal; IdCard card; };
整理了下,句柄类和计数器类应该具备的哪些内容.以下表列出: