自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C++基礎(chǔ)之面向?qū)ο缶幊趟枷耄ǘ?/h1>

開發(fā) 后端
本文介紹的是以C++程序語(yǔ)言為基礎(chǔ)的面向?qū)ο蟮木幊趟枷?。希望?duì)你有幫助,一起來(lái)看吧!

我們都知道,C++中最重要的概念——類,了解了類之后,已經(jīng)可以開始做些編程方面比較高級(jí)的應(yīng)用——設(shè)計(jì)程序,而不再只是將算法變成代碼。要說(shuō)明如何設(shè)計(jì)程序,有必要先了解何謂面向?qū)ο缶幊趟枷?/strong>。建議大家閱讀這一系列的文章,供大家參考。接上一篇>>

面向?qū)ο缶幊趟枷?/strong>

前面已說(shuō)明設(shè)計(jì)程序就是編寫程序欲解決的問(wèn)題的描述,也就是編寫論調(diào)。而論調(diào)可以只用“名詞性概念”和“動(dòng)詞性概念”表現(xiàn)出來(lái),對(duì)象又正好是“名詞性概念”的實(shí)現(xiàn),而利用前面說(shuō)的沒有成員變量的類來(lái)映射“動(dòng)詞性概念”就可以將其轉(zhuǎn)換為對(duì)象。因此,一個(gè)世界,可以完全由對(duì)象組成,而將算法所基于的世界只用對(duì)象表現(xiàn)出來(lái),再進(jìn)行后續(xù)代碼的編寫,這種編程方法就被稱作面向?qū)ο蟮木幊趟枷搿?/p>

注意,先設(shè)計(jì)算法應(yīng)基于的世界,再全部用對(duì)象將其表述出來(lái),然后再設(shè)計(jì)算法,最后映射為代碼。但前面在編寫商人過(guò)河問(wèn)題時(shí)是直接給出算法的,并沒有設(shè)計(jì)世界啊?其實(shí)由于那個(gè)問(wèn)題的過(guò)于簡(jiǎn)單,我直接下意識(shí)地設(shè)計(jì)了世界,并且用前面所說(shuō)的河岸論來(lái)描述它。應(yīng)注意世界的設(shè)計(jì)完全依賴于問(wèn)題,而準(zhǔn)確地說(shuō),前面我并沒有設(shè)計(jì)世界,而是設(shè)計(jì)了河岸論來(lái)描述問(wèn)題。

接著,由于對(duì)象就是實(shí)例,因此以對(duì)象來(lái)描述世界在C++中就是設(shè)計(jì)類,通過(guò)類的實(shí)例來(lái)組合表現(xiàn)世界。但應(yīng)注意,面向?qū)ο笫且詫?duì)象來(lái)描述世界,但也描述算法,因?yàn)樗惴ㄒ矔?huì)提出一些需要被映射的概念,如前面商人過(guò)河問(wèn)題的算法中的過(guò)河方案。

但切記,當(dāng)描述算法時(shí)操作了描述世界時(shí)定義的類,則一定要保持那個(gè)類的設(shè)計(jì),不要因?yàn)樗惴ㄖ袑?duì)那個(gè)類的實(shí)例的操作過(guò)于復(fù)雜而將那部分算法映射為這個(gè)類的一個(gè)成員函數(shù),因?yàn)檫@嚴(yán)重遮蔽了算法的實(shí)現(xiàn),破壞了程序的架構(gòu)。如一個(gè)算法是讓汽車原地不停打轉(zhuǎn),需要復(fù)雜的操作,那么難道給汽車加一個(gè)功能,讓它能原地不停地打轉(zhuǎn)?!這是在設(shè)計(jì)類的時(shí)候經(jīng)常犯的錯(cuò)誤,也由于這個(gè)原因,一個(gè)面向?qū)ο缶帉懙拇a并不是想象的只由類組成,其也可能由于將算法中的某些操作映射成函數(shù)而有大量的全局函數(shù)。

請(qǐng)記?。涸O(shè)計(jì)類時(shí),如果是映射世界里的概念,不要考慮算法,只以這個(gè)世界為邊界來(lái)設(shè)計(jì)它,不要因?yàn)樗惴ɡ锏哪硞€(gè)需要而給它加上錯(cuò)誤的成員。

因此,將“名詞性概念”映射成類,“名詞性概念”的屬性和狀態(tài)映射為成員變量,“名詞性概念”的功能映射為成員函數(shù)。那么“動(dòng)詞性概念”怎么辦?映射成沒有成員變量的類?前面也看見,由于過(guò)于別扭,實(shí)際中這種做法并不常見(STL中也只是將其作為一種技巧),故經(jīng)常是將它映射為函數(shù),雖然這有背于面向?qū)ο蟮乃枷?,但要易于理解得多,進(jìn)而程序的架構(gòu)要簡(jiǎn)明得多。

隨著面向?qū)ο缶幊趟枷氲膯?wèn)世,一種全新的設(shè)計(jì)方式誕生了。由于它是如此的好以至于廣為流傳,但理解的錯(cuò)誤導(dǎo)致錯(cuò)誤的思想遍地而生,更糟糕的就是本末倒置,將這個(gè)設(shè)計(jì)方式稱作面向?qū)ο蟮木幊趟枷?,它的名字就是封裝。

封裝

先來(lái)看現(xiàn)在在各類VC教程中關(guān)于對(duì)象的講解中經(jīng)常能看見的如下的一個(gè)類的設(shè)計(jì)。

 

 

  1. class Person  
  2. privatechar m_Name[20]; unsigned long m_Age; bool m_Sex;  
  3. public:  const char* GetName() const;  void SetName( const char* );  
  4. unsigned long GetAge() constvoid SetAge( unsigned long );  
  5. bool GetSex() const;  void SetSex( bool );  
  6.   }; 

 

上面將成員變量全部定義為private,然后又提供三對(duì)Get/Set函數(shù)來(lái)存取上面的三個(gè)成員變量(因?yàn)樗鼈兪莗rivate,外界不能直接存?。@三對(duì)函數(shù)都是public的,為什么要這樣?那些教材將此稱作封裝,是對(duì)類Person的內(nèi)部?jī)?nèi)存布局的封裝,這樣外界就不知道其在內(nèi)存上是如何布局的并進(jìn)而可以保證內(nèi)存的有效性(只由類自身操作其實(shí)例)。

首先要確認(rèn)上面設(shè)計(jì)的荒謬性,它是正宗的“有門沒鎖”毫無(wú)意義。接著再看所謂的對(duì)內(nèi)存布局的封裝。假設(shè)上面是在Person.h中的聲明,然后在b.cpp中要使用類Person,本來(lái)要#include "Person.h",現(xiàn)在替換成下面:

 

  1. class Person  
  2. publicchar m_Name[20]; unsigned long m_Age; bool m_Sex;  
  3. publicconst char* GetName() const;  void SetName( const char* );  
  4. unsigned long GetAge() constvoid SetAge( unsigned long );  
  5. bool GetSex() const;  void SetSex( bool );  
  6.   }; 

然后在b.cpp中照常使用類Person,如下:

 

  1. Person a, b; a.m_Age = 20; b.GetSex(); 

這里就直接使用了Person::m_Age了,就算不做這樣蹩腳的動(dòng)作,依舊#include "Person.h",如下:

 

 

  1. struct PERSON { char m_Name[20]; unsigned long m_Age; bool m_Sex; };  
  2. Person a, b; PERSON *pP = ( PERSON* )&a; pP->m_Age = 40; 

 

上面依舊直接修改了Person的實(shí)例a的成員Person::m_Age,如何能隱藏內(nèi)存布局?!請(qǐng)回想聲明的作用,類的內(nèi)存布局是編譯器生成對(duì)象時(shí)必須的,根本不能對(duì)任何使用對(duì)象的代碼隱藏有關(guān)對(duì)象實(shí)現(xiàn)的任何東西,否則編譯器無(wú)法編譯相應(yīng)的代碼。

那么從語(yǔ)義上來(lái)看。Person映射的不是真實(shí)世界中的人的概念,應(yīng)該是存放某個(gè)數(shù)據(jù)庫(kù)中的某個(gè)記錄人員信息的表中的記錄的緩沖區(qū),那么緩沖區(qū)應(yīng)該具備那三對(duì)Get/Set所代表的功能嗎?緩沖區(qū)是緩沖數(shù)據(jù)用的,緩沖后被其它操作使用,就好像箱子,只是放東西用。

故上面的三對(duì)Get/Set沒有存在的必要,而三個(gè)成員變量則不能是 private。當(dāng)然,如果Person映射的并不是緩沖區(qū),而在其它的世界中具備像上面那樣表現(xiàn)的語(yǔ)義,則像上面那樣定義就沒有問(wèn)題,但如果是因?yàn)閷?duì)內(nèi)存布局的封裝而那樣定義類則是大錯(cuò)特錯(cuò)的。

上面錯(cuò)誤的根本在于沒有理解何謂封裝。為了說(shuō)明封裝,先看下MFC(Microsoft Foundation Class Library——微軟功能類庫(kù),一個(gè)定義了許多類的庫(kù)文件,其中的絕大部分類是封裝設(shè)計(jì)。關(guān)于庫(kù)文件在說(shuō)明SDK時(shí)闡述)中的類CFile的定義。

從名字就可看出它映射的是操作系統(tǒng)中文件的概念,但它卻有這樣的成員函數(shù)——CFile::Open、CFile::Close、CFile::Read、 CFile::Write,有什么問(wèn)題?這四個(gè)成員函數(shù)映射的都是對(duì)文件的操作而不是文件所具備的功能,分別為打開文件、關(guān)閉文件、從文件讀數(shù)據(jù)、向文件寫數(shù)據(jù)。這不是和前面說(shuō)的成員函數(shù)的語(yǔ)義相背嗎?

上面四個(gè)操作有個(gè)共性,都是施加于文件這個(gè)資源上的操作,可以將它們叫做“被功能”,如文件具有“被打開”的功能,具有“被讀取”的功能,但應(yīng)注意它們實(shí)際并不是文件的功能。

按照原來(lái)的說(shuō)法,應(yīng)該將文件映射為一個(gè)結(jié)構(gòu),如FILE,然后上面的四個(gè)操作應(yīng)映射成四個(gè)函數(shù),再利用名字空間的功能,如下:

 

 

  1. namespace OFILE  
  2. {  
  3. bool Open( FILE&, … );  bool Close( FILE&, … );  
  4. bool Read( FILE&, … );  bool Write( FILE&, … );  

 

上面的名字空間OFILE表示里面的四個(gè)函數(shù)都是對(duì)文件的操作,但四個(gè)函數(shù)都帶有一個(gè)FILE&的參數(shù)?;叵敕庆o態(tài)成員函數(shù)都有個(gè)隱藏的參數(shù)this,因此,一個(gè)了不起的想法誕生了。

將所有對(duì)某種資源的操作的集合看成是一種資源,把它映射成一個(gè)類,則這個(gè)類的對(duì)象就是對(duì)某個(gè)對(duì)象的操作,此法被稱作封裝,而那個(gè)類被稱作包裝類或封裝類。

很明顯,包裝類映射的是“對(duì)某種資源的操作”,是一抽象概念,即包裝類的對(duì)象都是無(wú)狀態(tài)對(duì)象(指邏輯上應(yīng)該是無(wú)狀態(tài)對(duì)象,但如果多個(gè)操作間有聯(lián)系,則還是可能有狀態(tài)的,但此時(shí)它的語(yǔ)義也相應(yīng)地有些變化。如多一個(gè)CFile::Flush成員函數(shù),用于刷新緩沖區(qū)內(nèi)容,則此時(shí)就至少有一個(gè)狀態(tài)——緩沖區(qū),還可有一個(gè)狀態(tài)記錄是否已經(jīng)調(diào)用過(guò)CFile::Write,沒有則不用刷新)。

現(xiàn)在應(yīng)能了解封裝的含義了。將對(duì)某種資源的操作封裝成一個(gè)類,此包裝類映射的不是世界中定義的某一“名詞性概念”,而是世界的“動(dòng)詞性概念”或算法中“對(duì)某一概念的操作”這個(gè)人為定出來(lái)的抽象概念。由于包裝類是對(duì)某種資源的操作的封裝,則包裝類對(duì)象一定有個(gè)屬性指明被操作的對(duì)象,對(duì)于MFC中的 CFile,就是CFile::m_hFile成員變量(類型為HANDLE),其在包裝類對(duì)象的主要運(yùn)作過(guò)程(前面的CFile::Read和 CFile::Write)中被讀。

有什么好處?封裝提供了一種手段以將世界中的部分“動(dòng)詞性概念”轉(zhuǎn)換成對(duì)象,使得程序的架構(gòu)更加簡(jiǎn)單(多條“動(dòng)詞性概念”變成一個(gè)“名詞性概念”,減少了“動(dòng)詞性概念”的數(shù)量),更趨于面向?qū)ο蟮木幊趟枷搿?/p>

但應(yīng)區(qū)別開包裝類對(duì)象和被包裝的對(duì)象。包裝類對(duì)象只是個(gè)外殼,而被包裝的對(duì)象一定是個(gè)具有狀態(tài)的對(duì)象,因?yàn)椴僮骶褪歉淖冑Y源的狀態(tài)。對(duì)于 CFile,CFile的實(shí)例是包裝類對(duì)象,其保持著一個(gè)對(duì)被包裝對(duì)象——文件內(nèi)核對(duì)象(Windows操作系統(tǒng)中定義的一種資源,用HANDLE的實(shí)例表征)——的引用,放在CFile::m_hFile中。

因此,包裝類對(duì)象是獨(dú)立于被包裝對(duì)象的。即CFile a;,此時(shí)a.m_hFile的值為0或-1,表示其引用的對(duì)象是無(wú)效的,因此如果a.Read( … );將失敗,因?yàn)椴僮魇┘拥馁Y源是無(wú)效的。

對(duì)此,就應(yīng)先調(diào)用a.Open( … );以將a和一特定的文件內(nèi)核對(duì)象綁定起來(lái),而調(diào)用a.Close( … );將解除綁定。注意CFile::Close調(diào)用后只是解除了綁定,并不代表a已經(jīng)被銷毀了,因?yàn)閍映射的并不是文件內(nèi)核對(duì)象,而是對(duì)文件內(nèi)核對(duì)象操作的包裝類對(duì)象。

如果仔細(xì)想想,就會(huì)發(fā)現(xiàn),老虎能夠吃兔子,兔子能夠被吃,那這里應(yīng)該是老虎有個(gè)功能是“吃兔子”還是多個(gè)兔子的包裝類來(lái)封裝“吃兔子”的操作?

這其實(shí)不存在任何問(wèn)題,“老虎吃兔子”和“兔子被吃”完全是兩個(gè)不同的操作,前者涉及兩種資源,后者只涉及一種資源,因此可以同時(shí)實(shí)現(xiàn)兩者,具體應(yīng)視各自在相應(yīng)世界中的語(yǔ)義。如果對(duì)于真實(shí)世界,則可以簡(jiǎn)略地說(shuō)老虎有個(gè)“吃”的功能,可以吃“肉”,而動(dòng)物從“肉”和“自主能動(dòng)性”多重繼承,兔子再?gòu)膭?dòng)物繼承。

這里有個(gè)類叫“自主能動(dòng)性”,指動(dòng)物具有意識(shí),能夠自己動(dòng)作,這在C++中的表現(xiàn)就是有成員函數(shù)的類,表示有功能可以被操作,但收音機(jī)也具有調(diào)臺(tái)等功能,難道說(shuō)收音機(jī)也能自己動(dòng)?!這就是世界的意義——運(yùn)轉(zhuǎn)。

希望通過(guò)本文的介紹,能夠?qū)δ阌袔椭?/p>

責(zé)任編輯:于鐵 來(lái)源: 互聯(lián)網(wǎng)
相關(guān)推薦

2011-07-14 17:24:10

C++面向?qū)ο笏枷?/a>

2011-08-04 11:04:14

Objective-C 面向?qū)ο? 繼承

2023-04-26 00:15:32

python面向?qū)ο?/a>java

2010-01-20 09:48:44

面向?qū)ο?/a>

2010-08-24 16:00:43

C語(yǔ)言

2025-04-02 03:11:00

Python函數(shù)C++

2024-01-03 13:38:00

C++面向?qū)ο缶幊?/a>OOP

2010-01-19 15:36:02

C++語(yǔ)言

2020-04-15 11:07:31

C語(yǔ)言對(duì)象思想

2011-05-25 10:59:26

Javascript繼承

2011-05-13 10:51:25

javascript

2020-12-24 08:36:14

JavaJava基礎(chǔ)

2009-06-22 13:48:00

Java編程思想面向?qū)ο?/a>

2011-07-05 14:42:46

java

2010-01-13 14:05:55

C++語(yǔ)言

2009-12-22 01:54:50

C++之父Bjarne Stro

2011-07-20 14:12:48

2009-11-23 19:24:01

PHP面向?qū)ο缶幊?/a>

2017-04-21 09:07:39

JavaScript對(duì)象編程

2012-01-17 09:34:52

JavaScript
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)