日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Object Pascal 中类型

發(fā)布時(shí)間:2025/3/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Object Pascal 中类型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Object Pascal 中類型的一些注意 2010-04-16 14:15

---------------------------------------------------------

原創(chuàng)文章,如轉(zhuǎn)載,請注明出處

---------------------------------------------------------

以下內(nèi)容為備忘,你可能早已知道或者早已注意。

1, 區(qū)分類的forward聲明和繼承自TObject的子類
例如:
type
TFirst = class;?? //forward聲明, 類TFirst的具體聲明在該聲明區(qū)域后部
TSecond = class?? //一個(gè)完整的類定義,定義了類型TSecond
end;
TThird = class(TObject); //同樣是一個(gè)完整的類型定義,定義了類型TThird,
???? //這個(gè)類不包含任何數(shù)據(jù)成員和方法

2, constructor 與 destructor
我談兩點(diǎn):用對象名和類名調(diào)用create的不同、構(gòu)造和析構(gòu)的虛與實(shí)
首先需要說的是,對象被動態(tài)分配內(nèi)存塊,內(nèi)存塊結(jié)構(gòu)由類型決定,同時(shí)類型也決定了對該類型的“合法”操作!
一個(gè)對象的構(gòu)造函數(shù)用來得到這個(gè)內(nèi)存塊。
<1>, 用對象名和類名調(diào)用create的不同
構(gòu)造函數(shù)是一個(gè)類方法,通常應(yīng)該由類來調(diào)用,如下:
AMan := TMan.Create;
這條語句,會在堆中分配內(nèi)存塊(當(dāng)然,它不僅僅干這些,實(shí)際上它還會把類型中所有的有序類型字段置0,
置所有字符串為空,置所有指針類型為nil,所有variant為Unassigned;實(shí)際上,構(gòu)造函數(shù)只是把內(nèi)存塊進(jìn)行
了清零,內(nèi)存塊清零意味著對所有的數(shù)據(jù)成員清零),并把這個(gè)內(nèi)存塊的首地址給AMan。
但如果你用下面的方式調(diào)用,
AMan2 := AMan.Create; //假設(shè)AMan已經(jīng)被構(gòu)造, 如未被構(gòu)造,會產(chǎn)生運(yùn)行時(shí)異常,
???????? //本質(zhì)上,對未構(gòu)造的對象的操作是對非法內(nèi)存的操作,結(jié)果不
???????? //可預(yù)知!
這實(shí)際上相當(dāng)于調(diào)用一個(gè)普通的方法,不會分配內(nèi)存塊,當(dāng)然也不會自動初始化。這和你調(diào)用下面的方法
類似,
??????? AMan.DoSomething; //DoSomething 為類 TMan的普通方法
當(dāng)然,構(gòu)造函數(shù)畢竟是函數(shù),它會有一個(gè)返回值,這個(gè)返回值就是對象的地址,所以AMan2和AMan指向同
一地址。
Note:不要試圖通過用對象名調(diào)用create方法來實(shí)現(xiàn)對象初始化!!因?yàn)榘褬?gòu)造函數(shù)當(dāng)做普通的方法來調(diào)用
并不會實(shí)現(xiàn)自動初始化,所以用對象名來調(diào)用create方法就顯得很雞肋了,當(dāng)然,某些場合,這是一種技巧。
<2>, 構(gòu)造和析構(gòu)的虛與實(shí)
構(gòu)造函數(shù)和析構(gòu)函數(shù)應(yīng)該是虛的還是實(shí)的?這很疑惑!
看代碼:
---------------------------------
type
TMan = class(TObject)
public
?? constructor Create;
?? destructor Destroy;
end;

TChinese = class(TMan)
public
?? constructor create;
?? destructor Destroy;
end;

TBeijing = class(TChinese)
public
?? constructor Create;
?? destructor Destroy;
end;
....
??????? var
?? AMan, AMan2, AMan3: TMan;
?? AChinese: TChinese;
?? ABeijing: TBeijing;
begin
?? AMan := TChinese.Create;
?? AMan2 := TMan.Create;
?? AMan3 := TBeijing.Create;
??
?? AMan.Free;
?? AMan2.Free;
?? AMan3.Free;
end;
如果構(gòu)造都是“實(shí)”的,無論如何,對于上面的用法,對象都可以被正確構(gòu)造。但是對于析構(gòu),上述代碼有
問題!如果加入測試代碼,你會發(fā)現(xiàn)所有的析構(gòu)方法根本不會被執(zhí)行。知道,Free方法繼承自TObject,其
源碼如下:
procedure TObject.Free;
begin
?? if Self <> nil then
?? Destroy;
end;
constructor TObject.Create;
begin
end;

destructor TObject.Destroy;
begin
end;
Note:通常,self內(nèi)置參數(shù)是指向?qū)ο蟊旧?#xff0c;而在類方法中self是指向類本身。
很顯然,在前面的代碼中, AMan, AMan2, AMan3不是nil,free方法執(zhí)行了。但是很遺憾的是,它所執(zhí)行的
Destroy其實(shí)是基類TObject.Destroy; 而TObject.Destroy什么也不做。要理解這一點(diǎn),需要理解類的內(nèi)存組織
結(jié)構(gòu),理解“隱藏”的概念。所謂隱藏,指的是基類和子類有同名的方法(不考慮虛、動態(tài)的情況), 子類的該名
方法隱藏基類的方法名。運(yùn)行時(shí),到底調(diào)用哪一個(gè)方法取決于調(diào)用者類型。如下代碼:
type
TCountry = class
public
?? procedure Test;
end;
TChina = class(TCountry)
public
?? procedure Test;
end;

....

var
?? ACoun, ACoun1: TCountry;
?? AChina: TChina;
begin
?? ACoun := TCountry.Create;
?? ACoun1 := TChina.Create;
?? AChina := TChina.Create;

?? ACoun.Test; //調(diào)用TCountry.Test
?? ACoun1.Test; //調(diào)用TCountry.Test
?? AChina.Test; //調(diào)用TChina.Test

?? ACoun.Free;
?? AChina.Free;
end;
對于隱藏的情況,具體調(diào)用哪一個(gè)方法,取決于調(diào)用者本身的類型。
很顯然,一個(gè)實(shí)的析構(gòu)方法有問題!可能會造成內(nèi)存泄露。那么它們是虛的好了。問題來了,
type
TMan = class(TObject)
public
?? constructor Create;
?? destructor Destroy; virtual; override;
end;

TChinese = class(TMan)
public
?? constructor create;
?? destructor Destroy; virtual; override;
end;

TBeijing = class(TChinese)
public
?? constructor Create;
?? destructor Destroy; virtual; override;
end;
語法錯(cuò)誤!上述的代碼編譯器會提示語法錯(cuò)誤。ok,正確的應(yīng)該是下面
TMan = class(TObject)
public
?? constructor Create;
?? destructor Destroy; override;
end;

TChinese = class(TMan)
public
?? constructor create;
?? destructor Destroy; override;
end;

TBeijing = class(TChinese)
public
?? constructor Create;
?? destructor Destroy; override;
end;
疑問來了,不是只有virtual 和 dynamic 才能被覆蓋么?在Delphi中為了保證對象被完全的析構(gòu),
所有的Destroy方法“天生”為虛方法,可以在任何地方被覆蓋!雖然這破壞了語言語法上的一致性,
但這保證一個(gè)對象不管從哪里(繼承)來,都只有唯一的析構(gòu)方法。但是,編譯器不會強(qiáng)制你使用
override關(guān)鍵字,所以,你可以像下面這樣來定義析構(gòu)。
TChinaese = class(TMan)
public
?? destructor Destroy;
end;
這可能會帶來問題,雖然不總是會帶來問題。因?yàn)樵赥Chinese類中存在多個(gè)Destroy方法,分別是:
TObject.Destroy;
TMan.Destroy;
TChinese.Destroy;
所以,結(jié)論是,無論在什么地方定義Destroy,覆蓋它!!以確保整個(gè)類譜中只有一個(gè)Destroy;

****
至此,我們確信:一個(gè)實(shí)的構(gòu)造方法可以工作,一個(gè)實(shí)的析構(gòu)會有問題,析構(gòu)應(yīng)該總被override!
****
那么,虛的構(gòu)造可以正常工作么?看代碼:
type
TMan = class(TObject)
public
?? constructor Create; virtual;
end;

TChinese = class(TMan)
public
?? constructor create; override;
end;

TBeijing = class(TChinese)
public
?? constructor Create; override;
end;
....
??????? var
?? AMan, AMan2, AMan3: TMan;
?? AChinese: TChinese;
?? ABeijing: TBeijing;
begin
?? AMan := TChinese.Create;
?? AMan2 := TMan.Create;
?? AMan3 := TBeijing.Create;
??
?? AMan.Free;
?? AMan2.Free;
?? AMan3.Free;
end;
Ok,可以,工作正常。原因在于我們對對象的使用方法。我們總是用"確定"的類名來構(gòu)建的,這總可以保證
我們調(diào)用正確的方法。但如果我們使用類引用的話,情況不同了,如下:

type
?? TManClass = class of TMan;
var
?? AManClass: TManClass;
?? AObj, AObj2, AObj3: TMan;
begin
?? AManClass := TMan;?? //AManClass is TMan
?? AObj := AManClass.Create; //調(diào)用TMan.create

?? AManClass := TChinese; //AManClass is TChinese
?? AObj2 := AManClass.Create;??? //調(diào)用那一個(gè)create??

?? AManClass := TBeijing;
?? AObj3 := AManClass.Create;?? //which create???

?? ....
end;
和前面討論析構(gòu)的情況類似,這取決于方法的內(nèi)存布局,注意我在最初提到過類型決定布局。方法的內(nèi)存布局
取決于它是virtual, override, overload, dynamic.當(dāng)TMan.create 是virtual, 并且,TChinese.create是
override時(shí),AObj2 := AManClass.Create 調(diào)用的是 TChinese.Create; 如果不是,則是TMan.Create;

上面的解釋仍然是疑惑的,問題的關(guān)鍵在于什么是“類引用”!從語義上說,類引用是這樣一個(gè)類型:它代表了
一系列相關(guān)的存在繼承關(guān)系的類型,一個(gè)類引用變量代表一系列類型。區(qū)別于一般的變量,一般的變量表示的
是一系列實(shí)體,如:var count: integer; 意思是你定義了一個(gè)實(shí)例count,它的類型是integer; count 對應(yīng)
于堆或棧中的一個(gè)內(nèi)存塊,count是數(shù)據(jù)。這是一般情況下我們定義一個(gè)變量所隱含的意思。但類引用變量稍有
不同,如上,AManClass變量,它代表的是一系列和TMan兼容的“類型”,它的類型是TManClass,它沒有內(nèi)存塊
(你沒辦法說,一個(gè)類型的類型對應(yīng)什么內(nèi)存塊),實(shí)際上,可以認(rèn)為類引用類型指向了一個(gè)“代碼塊”。類引用
變量的值是“類”!這很重要(稍后會進(jìn)一步解釋)!Delphi對類引用的實(shí)現(xiàn)實(shí)際上就是一個(gè)32位的指針(一個(gè)普通
指針),它指向了類的虛方法表(VMT).
類引用的值是“類”,這決定了任何時(shí)候使用類引用變量只能調(diào)用類方法!一個(gè)構(gòu)造函數(shù)是類方法,所以使用類
引用調(diào)用Create方法是合理的,但是你不可以使用類引用調(diào)用非類方法,比如Destroy方法,實(shí)際上在類引用中
你也找不到Free方法。
需要提及的是,構(gòu)造函數(shù)雖然很接近于類方法,甚至于你也可以使用類方法來模擬構(gòu)造函數(shù):
TMan = class
public
?? class function Create: TMan;
end;

class function TMan.Create: TMan; //模擬構(gòu)造函數(shù)
begin
?? result := inherited Create;
end;
但構(gòu)造函數(shù)做的更多,除了前面提到的自動初始化外,構(gòu)造函數(shù)還會在構(gòu)造對象時(shí)將對象的VMT指針指向類的VMT。
此外,構(gòu)造函數(shù)中的self指的是對象本身,類方法中self指的是類本身。總之,我們可以認(rèn)為構(gòu)造函數(shù)是一個(gè)
特殊的類函數(shù)。

Ok,到此,我們確信:類引用可以調(diào)用類方法,構(gòu)造函數(shù)可以使用類引用來調(diào)用。
問題產(chǎn)生:由于類引用表示的是“一系列”類型,那么調(diào)用構(gòu)造方法的時(shí)候,到底調(diào)用的是那一個(gè)構(gòu)造方法呢?
對于TManClass類型變量而言,它只能調(diào)用的一定是TMan類的create方法,但如果TMan類的子類覆蓋了TMan的create
方法,調(diào)用TMan類的create方法實(shí)際上就是調(diào)用子類的create方法。這里的關(guān)鍵是理解“覆蓋”的概念。這實(shí)際上意味
著對使用類引用調(diào)用構(gòu)造方法,虛的構(gòu)造方法和實(shí)的構(gòu)造方法是不同的。對于前面的例子而言,
AManClass := TChinese;
AObj2 := AManClass.create; //調(diào)用了TChinese.Create;

AManClass := TBeijing;
AObj3 := AManClass.create; //調(diào)用了TBeijing.Create;
但如果構(gòu)造不是覆蓋虛函數(shù),那么它們統(tǒng)統(tǒng)是調(diào)用TMan.Create;顯然這不是我們期望的。
Note:每個(gè)類有一個(gè)虛方法表(VMT),而不是每個(gè)對象有一個(gè)!

結(jié)論:構(gòu)造函數(shù)是可以被類本身、類引用、對象調(diào)用,在這三種情況下構(gòu)造函數(shù)的表現(xiàn)是不同的,在使用類引用的情況
下虛的構(gòu)造通常是必要的。

那么,構(gòu)造函數(shù)總是虛的是否更合理呢?我個(gè)人傾向于總是虛的更合理,因?yàn)檫@樣可以保證一個(gè)對象總是被正確的構(gòu)造。
實(shí)際上,VCL中組件繼承的大多Create都被聲明成虛方法了。但Delphi并未將TObject的構(gòu)造實(shí)現(xiàn)為虛,也許是出于兼容的
考慮,我不太清楚。我聽說,Java中的構(gòu)造總是虛的。當(dāng)然,總是虛的也有弊端,例如:你不可以通過調(diào)用Create方法來
只初始化父類的成員,因?yàn)樵陬惖睦^承體系中只有一個(gè)Create方法。但是靜態(tài)的構(gòu)造可以實(shí)現(xiàn)父類的成員初始化。私下里,
我猜想也許是因?yàn)闃?gòu)造函數(shù)承擔(dān)了太多的義務(wù),使得Delphi設(shè)計(jì)者沒有像析構(gòu)那樣以總是虛來處理。

另,在C++中有一條法則,不要在構(gòu)造中使用虛!但在Delphi中可以隨便,根本原因在于RTTI(運(yùn)行時(shí)類型信息),Delphi在
運(yùn)行時(shí)保存了完整的類型信息。這是C++和Delphi的重要不同之處。

?

總結(jié):對析構(gòu)函數(shù)而言,無論是否聲明為virtual,它總是虛的,你總需要使用override;對構(gòu)造函數(shù)而言,虛和實(shí)取決
于的使用方法,但一般而言使用虛更安全,在使用類引用的情況下一定要保證子類覆蓋基類的構(gòu)造方法。

總結(jié)

以上是生活随笔為你收集整理的Object Pascal 中类型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 美女脱了裤子让男人桶 | 性chinese天美传媒麻 | 久久精品在线免费观看 | 少妇喷水在线观看 | 男女无套免费视频网站动漫 | 黄色男女网站 | 少妇免费毛片久久久久久久久 | 日本精品网站 | 国产wwwxxx | 五月婷婷免费视频 | 色眯眯av | 999久久久精品视频 亚洲视频精品在线 | 五月天色综合 | 精品国产理论 | 一区二区三区不卡在线观看 | 中国丰满人妻videoshd | 国产精品每日更新 | 午夜剧场在线 | 青草99 | 蜜臀一区二区三区精品免费视频 | 免费高清毛片 | 一本一道av | av天堂一区| 99热在| 日本伦理片在线播放 | 日韩一区高清 | 天天躁日日躁狠狠躁喷水 | 瑟瑟视频网站 | 天天插天天透 | 免费在线黄色网 | 男人的天堂视频在线观看 | 成人春色影视 | 91大神在线看 | 台湾swag在线播放 | 人人妻人人澡人人爽久久av | 日本精品少妇 | 亚洲精品视频播放 | 色婷综合| 欧美成人一区二区三区四区 | 黄色片亚洲| 亚洲人成电影一区二区在线 | 国产99久久久国产精品成人免费 | 欧美二级片 | 精品人妻午夜一区二区三区四区 | 欧美系列一区二区 | 一本大道av伊人久久综合 | 国产粉嫩白浆 | 亚洲www视频 | 国产一级黄色大片 | 日产电影一区二区三区 | 九九爱视频 | 在线观看中文 | 69亚洲精品久久久蜜桃小说 | 欧美影视一区二区 | 亚洲色图丝袜美腿 | 激情噜噜 | 日本做爰三级床戏 | 欧美一级片在线看 | 日本三级欧美三级 | 久久免费高清视频 | 亚洲成人一二三区 | 亚洲欧美精品久久 | 99re这里只有精品66 | 人人爽人人草 | 丁香综合 | 亚洲精品97久久中文字幕无码 | 亚洲三级小说 | 久久国产精品系列 | 欧美乱妇日本无乱码特黄大片 | 婷婷五月综合激情 | 欧美性生活视频 | 男女啪啪在线观看 | 国产自产自拍 | 视频在线观看视频 | 久久久久久久久久久久久久国产 | 中文字幕av亚洲精品一部二部 | 日本视频不卡 | 亚洲精品无码久久久久久久 | 在线观看小视频 | 欧美一区三区二区在线观看 | 东京热无码av一区二区 | www.白浆| av在线浏览| 最近最好的2019中文 | 午夜影视在线观看 | 国产精品宾馆在线精品酒店 | 久久国产99| 日本大尺度做爰呻吟 | 国产探花在线观看 | 青青草国产 | 中文字幕无线精品亚洲乱码一区 | 国产午夜免费福利 | 蕾丝视频污 | 国产精品国产三级国产专区52 | 成人片片 | 精品av一区二区 | 国内精品在线观看视频 | 久久久av一区二区三区 | 久草免费在线观看视频 |