C++代理 Surrogate

容器通常只能包含一種類型的對象,所以很難在容器中存儲對象本身。存儲指向對象的指針,雖然允許通過繼承來處理類型不同的問題(?多態性?),但是也增加了內存分配的額外負擔。所以我們通過定義名為?代理?的對象來解決該問題?。代理?運行起來和它所代表的對象基本相同,但是允許將整個派生層次壓縮在一個對象類型中。

假設有一個表示不同種類的交通工具的類派生層次:

class Vehicle
{
public:virtual double weight() const = 0;virtual void start() = 0;//...
};class RoadVehicle:public Vehicle{/*...*/};
class AutoVehicle:public Vehicle{/*...*/};
class Aircraft:public Vehicle{/*...*/};
class Helicopter:public Vehicle{/*...*/};

可見Vehicle是一個抽象基類,有兩個純虛函數表示一些共有屬性。下面請看下面這句話為什么不能達到預期的效果:

Vehicle parking_lot[1000];

表面上看是由于Vehicle是一個抽象基類,因此,只有從類Vehicle派生出來的類才能實例化,類Vehicle本身不會有對象,自然也就不會有對象數組了。

???????但是,假設我們剔除了類Vehicle中的所有純虛函數,使其對象存在,那又會出現什么樣的情況呢?看下面的語句:

Automobile x=/*...*/;

parking_lot[num_vehicles++] = x;

把x賦給parking_lot的元素,會把x轉換成一個Vehicle對象,同時會丟失所有在Vehicle類中沒有的成員。該賦值語句還會把這個被剪裁了的對象復制到parking_lot數組中去。這樣,我們只能說parking_lot是Vehicle的集合,而不是所有繼承自Vehicle的對象的集合。

經典解決方案------提供一個間接層

最早的合適的間接層形式就是存儲指針,而不是對象本身:

Vehicle* parking_lot[1000];

然后,就有

Automobile x = /*...*/;

parking_lot[num_vehicles++] = &x;

這種方法解決了迫切的問題,但是也帶來了兩個新問題。

我們存儲在parking_lot中的是指向x的指針,在上例中是一個局部變量。這樣,一旦變量x沒有了【作用域】,parking_lot就不知道指向什么東西了。

我們可以這么變通一下,放入parking_lot中的值,不是指向原對象的指針,而是指向它們的副本的指針。當我們釋放parking_lot時,也釋放其中所指向的全部對象。

上述修改雖然不用存儲指向本地對象的指針,但是它也帶來了動態內存管理的負擔。另外,只有當我們知道要放到parking_lot中的對象的靜態類型后,這種方法才起作用。不知道又會怎樣呢?看下面的:

if(p != q)

{

?? delete parking_lot[p];

???parking_lot[p] = parking_lot[q];

}

這樣的話,parking_lot[p]和parking_lot[q]將指向相同的對象,這不是我們想要的。在看下面的行不行:

if(p != q)

{

?? delete parking_lot[p];

???parking_lot[p] = new Vehicle(*parking_lot[q]);

}

這樣我們又回到了前面的問題:沒有Vehicle類型的對象,即使有,也不是我們想要的(是經過剪裁后的對象)。

如何復制編譯時類型未知的對象-------虛復制函數

我們在上面的Vehicle類中加入一個合適的純虛函數:

class Vehicle?
{?
public:?
virtual double weight() const = 0;?
virtual void start() = 0;

virtual Vehicle* copy() const = 0;?
?//...?
};

接下來,在每個派生自Vehicle的類中添加一個新的成員函數copy。指導思想就是,如果vp指向某個繼承自Vehicle的不確定類的對象,那么vp->copy()會獲得一個指針,該指針指向該對象的一個新建的副本。例如:如果Truck繼承自(間接或直接)類Vehicle,則它的copy函數就類似于:

Vehicle* Truck::copy() const

{

??? return new Truck(*this);

}

當然,處理完一個對象后,需要清除該對象。要做到這一點,就必須確保類Vehicle有一個虛析構函數:

class Vehicle?
{?
public:?
virtual double weight() const = 0;?
virtual void start() = 0;

virtual Vehicle* copy() const = 0;

virtual ~Vehicle(){}?
?//...?
};?
有了上面的分析,下面我們就來定義代理類:

class VehicleSurrogate
{
public:
  VehicleSurrogate();
  VehicleSurrogate(const Vehicle&);
  ~VehicleSurrogate();
  VehicleSurrogate(const VehicleSurrogate&);
  VehicleSurrogate& operator = (const VehicleSurrogate&);
private:
  Vehicle* vp;
};

上述代理類有一個以const Vehicle&為參數的構造函數,這樣就能為任意繼承自Vehicle的類的對象創建代理了?(多態性,因為這里是引用參數)?。同時,代理類還有一個缺省構造函數,所以我們能夠創建VehicleSurrogate對象的數組。

然而,?缺省構造函數也給我們帶來了問題:如果Vehicle是個抽象基類,我們應該如何規定VehicleSurrogate的缺省操作呢?它所指向的對象的類型是什么呢?不可能是Vehicle,因為根本就沒有Vehicle對象。為了得到一個更好的方法,我們要引入行為類似于零指針的空代理的概念。能夠創建、銷毀和復制這樣的代理,但是進行其他的操作就視為出錯。

下面看各個函數的定義:

VehicleSurrogate::VehicleSurrogate():vp(0){}

VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()){}//非零的檢測室必要的,空代理

VehicleSurrogate::~VehicleSurrogate()

{

????? delete vp;//C++標準里面對一個空指針運用delete也是沒有問題的

}?
VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v):vp(v.vp?v.vp->copy():0){}

VehicleSurrogate& VehicleSurrogate::operator=(const VehicleSurrogate& v)

{

??? if(this!=&v)//對賦值操作符進行檢測,確保沒有將代理賦值給它自身

??? {

????? delete vp;

????? vp=(v.vp?v.vp->copy():0);//非零的檢測是必要的,空代理

??? }

??? return *this;

}

下面就很容易定義我們的數組了:

VehicleSurrogate parking_lot[1000];

Automobile x;

parking_lot[num_vehicles++] = x;

最后一條語句就等價于

parking_lot[num_vehicles++] = VehicleSurrogate(x);

這個語句創建了一個關于對象x的副本,并將VehicleSurrogate對象綁定到該副本,然后將這個對象賦值給parking_lot的一個元素。當最后銷毀parking_lot數組時,所有這些副本也將被清除。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458670.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458670.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458670.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ Handle(句柄) part1

本文是我學習C++沉思錄第6章的筆記 本文主要講述了Handle類的概念,定義方法以及寫時復制技術。 在前文(Surrogate代理類)的講解中我們了解到了代理的實現方法. 代理類有很多好處,但是麻煩的是每次都得進行復制.如果該類是經常使用并且member很…

sscanf的高級用法

sscanf的高級用法(總結) 2012-04-25 18:50:25分類: C/C sscanf(recvbuf,"%*[^/]/%[^ ]s",buf_rev); sscanf(buf, "GET /%[^ ]", buf_rev);這個是在一個webserver.c里面的例子,通過sscanf(&#xf…

選擇排序 冒泡排序 二分查找

選擇排序 int [] arr {2,48,28,32,90,12}; for&#xff08;int i 0&#xff1b; i < arr.length - 1;i&#xff09;{ for(int j i 1; j < arr.length;j){ if(arr[i] < arr[j]){ int c; c arr[i]; arr[i] arr[j]; arr[j] c; } } } 冒泡排序 for(int i 0;i <…

C++, ID、指針、handle (void *)的區別

原文鏈接&#xff1a; http://hi.baidu.com/dandanfeng160/blog/item/4eaa3df5215bc42dbd310955.html 在Windows程序設計中&#xff0c;句柄是無法精確定義的術語。隨便找一個高手&#xff0c;讓他給你講講句柄是什么&#xff0c;恐怕他都很難給你一個具體的定義來。 在Wind…

Swift調用Objective C的FrameWork

很多Github的庫經過很多年的發展&#xff0c;源碼都是OC寫的&#xff0c;&#xff0c;所以&#xff0c;用Swift調用OC的庫就是開發中難免遇到的的一個問題&#xff0c;本文以AFNetworking為例&#xff0c;講解如何跨語言調用。 第一步 創建一個空的工程 注意&#xff0c;語言選…

命令行 java文本編輯工具 重載 內存區域 棧 堆

一、dir 列出當前目錄下的文件以及文件夾 md創建目錄 rd刪除目錄 cd 進入指定目錄 cd..返回到上一級目錄 &#xff1a; 切換盤符 比如&#xff1a; F: 二、editPlus 編寫程序 三、重載&#xff1a;在同一個class中&#xff0c;出現了函數名稱相同&#xff0…

數據結構(Java)——查找和排序(1)

1.查找的定義 查找是這樣一個過程&#xff0c;即在某個項目組中尋找某一指定目標元素&#xff0c;或者確定該組中并不存在該目標元素。 對其進行查找的項目的組有時也成為查找池。兩種常見的查找方式&#xff1a;線性查找和二分查找。為了能夠查找某一對象&#xff0c;我們就必…

GetProcAddress()用法

函數功能描述: GetProcAddress()函數檢索指定的動態鏈接庫(DLL)中的輸出庫函數地址。 函數原型&#xff1a; FARPROC GetProcAddress( HMODULE hModule, // DLL模塊句柄 LPCSTR lpProcName // 函數名 ); 參數&#xff1a; hModule [in] 包含此函數的…

支付寶問題LaunchServices: ERROR: There is no registered handler for URL scheme alipay

LaunchServices: ERROR: There is no registered handler for URL scheme alipay &#xff08;這句話其實是在告訴你 設備上沒有安裝 支付寶的客戶端,此時會走網頁端&#xff09;而有人會發現并沒有HTML5網頁彈出過一會&#xff0c;會發現服務器返回4000支付失敗&#xff0c;這…

C++string類常用函數 c++中的string常用函數用法總結

string類的構造函數&#xff1a; string(const char *s); //用c字符串s初始化 string(int n,char c); //用n個字符c初始化 此外&#xff0c;string類還支持默認構造函數和復制構造函數&#xff0c;如string s1&#xff1b;string s2"hello"&#xff1b;都是正…

排列與組合

話說&#xff0c;初一的時候看到這樣一道題&#xff1a;有一種彩票中獎率為1%&#xff0c;買一百張是不是一定能中獎&#xff1f;答案自然是否定的&#xff0c;但我在想&#xff0c;如果有200張彩票&#xff0c;兩張有獎&#xff0c;買一百張中獎率是多少&#xff1f;一天晚上睡…

剔除服務器返回的NSNull格式的數據

服務器返回NSNull格式的數據&#xff0c;真。。的煩人 解決辦法&#xff1a;在AFN請求里面加上下面兩段代碼&#xff0c;OK AFJSONResponseSerializer *response (AFJSONResponseSerializer *)manager.responseSerializer; response.removesKeysWithNullValues YES;

顯式(靜態)調用: LIB + DLL + .H

1、編程時用ad.h,ad.lib,放在項目當前目錄里2、在頭文件中加入#include "ad.h"3、在Project Setting–>Link–>Object/library modules加入ad.lib執行時將ad.dll跟你的程序放在同一目錄。 就可以直接調用dll中的函數了 當前目錄 轉載于:https://www.cnblogs.co…

boost Mutex

寫過多線程程序的人都知道&#xff0c;不能讓多個線程同時訪問共享的資源是至關重要的。 假如一個線程試圖改變共享數據的值&#xff0c;而另外一個線程試圖去讀取該共享數據的值&#xff0c;結果將是未定義的。 為了阻止這樣的事情發生&#xff0c;需要用到一些非凡的原始數據…

接入支付寶出現交易訂單處理失敗,請稍后再試(ALI64)的錯誤

上次在接入支付寶的時候就碰到了交易訂單處理失敗&#xff0c;請稍后再試&#xff08;ALI64&#xff09;這樣的錯誤&#xff0c;后來經過排查和總結&#xff0c;一般來講這種問題都是公鑰和私鑰沒有正確配置造成的。支付寶這邊為了保證數據在傳輸時不被篡改&#xff0c;使用了r…

c中session的用法

c中session的用法你知道嗎&#xff1f;下面小編就跟你們詳細介紹下c中session的用法&#xff0c;希望對你們有用。c中session的用法如下&#xff1a;Session的基本屬性&#xff1a;一、屬性1、SessionIDSessionID 屬性返回用戶的會話標識。在創建會話時&#xff0c;服務器會為每…

查看硬件信息

測試機器的硬件信息&#xff1a; 查看CPU信息&#xff08;型號&#xff09; # cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 8 Intel(R) Xeon(R) CPU E5410 2.33GHz (看到有8個邏輯CPU, 也知道了CPU型號) # cat /proc/cpuinfo | grep physical …

支付寶集成交互流程

交互流程 功能流程 流程說明&#xff08;以Android平臺為例&#xff09;&#xff1a; 第4步&#xff1a;調用支付接口&#xff1a;此消息就是本接口所描述的開發包提供的支付對象PayTask&#xff0c;將商戶簽名后的訂單信息傳進pay方法喚起支付寶收銀臺&#xff0c;訂單格式具體…

VxLAN基礎

轉自&#xff1a;http://blog.csdn.net/freezgw1985/article/details/16354897 一 . 為什么需要Vxlan1. vlan的數量限制4096個vlan遠不能滿足大規模云計算數據中心的需求2. 物理網絡基礎設施的限制基于IP子網的區域劃分限制了需要二層網絡連通性的應用負載的部署3. TOR交換機MA…

find_first_of()和 find_last_of() 【獲取路徑、文件名】

string 類提供字符串處理函數&#xff0c;利用這些函數&#xff0c;程序員可以在字符串內查找字符&#xff0c;提取連續字符序列(稱為子串)&#xff0c;以及在字符串中刪除和添加。我們將介紹一些主要函數。 1.函數find_first_of()和 find_last_of() 執行簡單的模式匹配&#x…