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