C++代理 Surrogate
容器通常只能包含一種類型的對(duì)象,所以很難在容器中存儲(chǔ)對(duì)象本身。存儲(chǔ)指向?qū)ο蟮闹羔?#xff0c;雖然允許通過繼承來處理類型不同的問題(?多態(tài)性?),但是也增加了內(nèi)存分配的額外負(fù)擔(dān)。所以我們通過定義名為?代理?的對(duì)象來解決該問題?。代理?運(yùn)行起來和它所代表的對(duì)象基本相同,但是允許將整個(gè)派生層次壓縮在一個(gè)對(duì)象類型中。
假設(shè)有一個(gè)表示不同種類的交通工具的類派生層次:
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是一個(gè)抽象基類,有兩個(gè)純虛函數(shù)表示一些共有屬性。下面請(qǐng)看下面這句話為什么不能達(dá)到預(yù)期的效果:
Vehicle parking_lot[1000];
表面上看是由于Vehicle是一個(gè)抽象基類,因此,只有從類Vehicle派生出來的類才能實(shí)例化,類Vehicle本身不會(huì)有對(duì)象,自然也就不會(huì)有對(duì)象數(shù)組了。
???????但是,假設(shè)我們剔除了類Vehicle中的所有純虛函數(shù),使其對(duì)象存在,那又會(huì)出現(xiàn)什么樣的情況呢?看下面的語(yǔ)句:
Automobile x=/*...*/;
parking_lot[num_vehicles++] = x;
把x賦給parking_lot的元素,會(huì)把x轉(zhuǎn)換成一個(gè)Vehicle對(duì)象,同時(shí)會(huì)丟失所有在Vehicle類中沒有的成員。該賦值語(yǔ)句還會(huì)把這個(gè)被剪裁了的對(duì)象復(fù)制到parking_lot數(shù)組中去。這樣,我們只能說parking_lot是Vehicle的集合,而不是所有繼承自Vehicle的對(duì)象的集合。
經(jīng)典解決方案------提供一個(gè)間接層
最早的合適的間接層形式就是存儲(chǔ)指針,而不是對(duì)象本身:
Vehicle* parking_lot[1000];
然后,就有
Automobile x = /*...*/;
parking_lot[num_vehicles++] = &x;
這種方法解決了迫切的問題,但是也帶來了兩個(gè)新問題。
①我們存儲(chǔ)在parking_lot中的是指向x的指針,在上例中是一個(gè)局部變量。這樣,一旦變量x沒有了【作用域】,parking_lot就不知道指向什么東西了。
我們可以這么變通一下,放入parking_lot中的值,不是指向原對(duì)象的指針,而是指向它們的副本的指針。當(dāng)我們釋放parking_lot時(shí),也釋放其中所指向的全部對(duì)象。
②上述修改雖然不用存儲(chǔ)指向本地對(duì)象的指針,但是它也帶來了動(dòng)態(tài)內(nèi)存管理的負(fù)擔(dān)。另外,只有當(dāng)我們知道要放到parking_lot中的對(duì)象的靜態(tài)類型后,這種方法才起作用。不知道又會(huì)怎樣呢?看下面的:
if(p != q)
{
?? delete parking_lot[p];
???parking_lot[p] = parking_lot[q];
}
這樣的話,parking_lot[p]和parking_lot[q]將指向相同的對(duì)象,這不是我們想要的。在看下面的行不行:
if(p != q)
{
?? delete parking_lot[p];
???parking_lot[p] = new Vehicle(*parking_lot[q]);
}
這樣我們又回到了前面的問題:沒有Vehicle類型的對(duì)象,即使有,也不是我們想要的(是經(jīng)過剪裁后的對(duì)象)。
如何復(fù)制編譯時(shí)類型未知的對(duì)象-------虛復(fù)制函數(shù)
我們?cè)谏厦娴腣ehicle類中加入一個(gè)合適的純虛函數(shù):
class Vehicle?
{?
public:?
virtual double weight() const = 0;?
virtual void start() = 0;
virtual Vehicle* copy() const = 0;?
?//...?
};
接下來,在每個(gè)派生自Vehicle的類中添加一個(gè)新的成員函數(shù)copy。指導(dǎo)思想就是,如果vp指向某個(gè)繼承自Vehicle的不確定類的對(duì)象,那么vp->copy()會(huì)獲得一個(gè)指針,該指針指向該對(duì)象的一個(gè)新建的副本。例如:如果Truck繼承自(間接或直接)類Vehicle,則它的copy函數(shù)就類似于:
Vehicle* Truck::copy() const
{
??? return new Truck(*this);
}
當(dāng)然,處理完一個(gè)對(duì)象后,需要清除該對(duì)象。要做到這一點(diǎn),就必須確保類Vehicle有一個(gè)虛析構(gòu)函數(shù):
class Vehicle?
{?
public:?
virtual double weight() const = 0;?
virtual void start() = 0;
virtual Vehicle* copy() const = 0;
virtual ~Vehicle(){}?
?//...?
};?
有了上面的分析,下面我們就來定義代理類:
上述代理類有一個(gè)以const Vehicle&為參數(shù)的構(gòu)造函數(shù),這樣就能為任意繼承自Vehicle的類的對(duì)象創(chuàng)建代理了?(多態(tài)性,因?yàn)檫@里是引用參數(shù))?。同時(shí),代理類還有一個(gè)缺省構(gòu)造函數(shù),所以我們能夠創(chuàng)建VehicleSurrogate對(duì)象的數(shù)組。
然而,?缺省構(gòu)造函數(shù)也給我們帶來了問題:如果Vehicle是個(gè)抽象基類,我們應(yīng)該如何規(guī)定VehicleSurrogate的缺省操作呢?它所指向的對(duì)象的類型是什么呢?不可能是Vehicle,因?yàn)楦揪蜎]有Vehicle對(duì)象。為了得到一個(gè)更好的方法,我們要引入行為類似于零指針的空代理的概念。能夠創(chuàng)建、銷毀和復(fù)制這樣的代理,但是進(jìn)行其他的操作就視為出錯(cuò)。
下面看各個(gè)函數(shù)的定義:
VehicleSurrogate::VehicleSurrogate():vp(0){}
VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()){}//非零的檢測(cè)室必要的,空代理
VehicleSurrogate::~VehicleSurrogate()
{
????? delete vp;//C++標(biāo)準(zhǔn)里面對(duì)一個(gè)空指針運(yùn)用delete也是沒有問題的
}?
VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v):vp(v.vp?v.vp->copy():0){}
VehicleSurrogate& VehicleSurrogate::operator=(const VehicleSurrogate& v)
{
??? if(this!=&v)//對(duì)賦值操作符進(jìn)行檢測(cè),確保沒有將代理賦值給它自身
??? {
????? delete vp;
????? vp=(v.vp?v.vp->copy():0);//非零的檢測(cè)是必要的,空代理
??? }
??? return *this;
}
下面就很容易定義我們的數(shù)組了:
VehicleSurrogate parking_lot[1000];
Automobile x;
parking_lot[num_vehicles++] = x;
最后一條語(yǔ)句就等價(jià)于
parking_lot[num_vehicles++] = VehicleSurrogate(x);
這個(gè)語(yǔ)句創(chuàng)建了一個(gè)關(guān)于對(duì)象x的副本,并將VehicleSurrogate對(duì)象綁定到該副本,然后將這個(gè)對(duì)象賦值給parking_lot的一個(gè)元素。當(dāng)最后銷毀parking_lot數(shù)組時(shí),所有這些副本也將被清除。?
總結(jié)
以上是生活随笔為你收集整理的C++代理 Surrogate的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 屏幕尺寸 分辨率
- 下一篇: C++中int *p[4]和 int (