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;
};

整理了下,句柄類和計數器類應該具備的哪些內容.以下表列出:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM