現(xiàn)代C++學(xué)習(xí)指南-具體類
類作為C++中重要的概念之一,有著眾多的特性,也是最迷人的部分!
類是一個(gè)加工廠,開發(fā)者使用C++提供的各種材料組裝這個(gè)工廠,使得它可以生產(chǎn)出符合自己要求的數(shù)據(jù),通過對工廠的改造,可以精細(xì)控制對象從出生到死亡的各種行為,真正達(dá)到我的代碼我做主的境界。
類
我們經(jīng)常說的面向?qū)ο笕筇卣鳎悍庋b,繼承和多態(tài),其實(shí)說的是一種抽象維度。最簡單的就是具體類,它將數(shù)據(jù)打包在一起,提供操作數(shù)據(jù)的函數(shù),使得開發(fā)者不再需要通過傳參的形式傳遞數(shù)據(jù)。它實(shí)現(xiàn)了事物的抽象,也就是所謂的封裝。第二層是在一堆數(shù)據(jù)中提取出共性的部分作為基類,然后將特性作為子類,充分利用繼承的優(yōu)點(diǎn),實(shí)現(xiàn)代碼復(fù)用。它不僅追求數(shù)據(jù)抽象,也追求行為上的相似性。而更進(jìn)一步,一套算法不關(guān)心實(shí)際的數(shù)據(jù),只關(guān)心它可以用來完成什么工作,甚至相互都不知道對方的存在,唯一的共同點(diǎn)就是都繼承自某個(gè)類,都能完成那個(gè)類指定的操作,至于細(xì)節(jié)都不關(guān)心,這就是多態(tài),類只是一種規(guī)范流程。從第一層到第三層,抽象的事物從具體轉(zhuǎn)向抽象,重心也從數(shù)據(jù)轉(zhuǎn)向行為,只是為了更好的可維護(hù)性和解耦性。三者的關(guān)系可能是下圖這樣的:

為了能將跟高級的繼承和多態(tài)講明白,本篇我們將著重介紹他們的第一形態(tài):封裝,也就是具體類。
類的基本組成
類是一種自定義類型,主要由兩部分組成:成員變量保存類管理的數(shù)據(jù),成員函數(shù)操作數(shù)據(jù)。
和普通變量相比,類中的成員變量最大的不同是其生命周期。成員變量在類實(shí)例化后才占用空間,構(gòu)造函數(shù)完成其初始化工作,在構(gòu)造完成后,成員函數(shù)就可以無限制地使用成員變量,直到析構(gòu)函數(shù)被調(diào)用。
成員函數(shù)和普通函數(shù)的不同之處是成員函數(shù)有個(gè)隱含的this指針,這個(gè)指針指向成員變量的存儲(chǔ)位置,也就是可以很方便地完成成員變量的訪問。
由此可見,具體類研究的主體是數(shù)據(jù)。接下來我將圍繞著數(shù)據(jù)的生命周期完成對類特性的解析。
對象的創(chuàng)建和銷毀
類的第一大作用就是控制類怎么生成和銷毀。和Java不同,不需要用new也會(huì)涉及到構(gòu)造函數(shù)的調(diào)用,哪怕只是個(gè)普通的局部變量,出了變量的作用范圍,對象就會(huì)被銷毀,內(nèi)存就會(huì)被釋放。
1class Sample{2 public:3 Sample(){4 std::cout<<"Creating a Sample object"<<std::endl;5 }67 ~Sample(){8 std::cout<<"Destoring a Sample object"<<std::endl;9 }10};1112int main(){13 // Sample的構(gòu)造函數(shù)被調(diào)用14 Sample a;15 {16 // 大括號創(chuàng)建了一個(gè)局部作用域,對象b只存在大括號范圍內(nèi),出了大括號后,b就會(huì)被銷毀,調(diào)用Sample的析構(gòu)函數(shù)17 Sample b;18 }19 // 此時(shí)只有對象a還存活20}21// 輸出22// Creating a Sample object23// Creating a Sample object24// Destoring a Sample object25// Destoring a Sample object
main函數(shù)中,創(chuàng)建了兩個(gè)變量。通過檢查輸出,我們可以確定類的構(gòu)造函數(shù)和析構(gòu)函數(shù)都被調(diào)用了。上面那個(gè)類從功能上毫無用處,我們只能創(chuàng)建一個(gè)它的對象,然后看著它死去,什么也干不了。接下來,我們來改造下
Sample類,讓它能在構(gòu)造的時(shí)候告訴我們,哪一個(gè)對象在構(gòu)造。1class Sample{2 Sample(const std::string name){3 std::cout<<"Creating a Sample object:name = "<<name<<std::endl;4 }5 //其余不變6};78int main(){9 // 由于創(chuàng)建對象a時(shí),用到了string對象,所以要先創(chuàng)建一個(gè)string對象10 std::string str{"a"};11 // 此時(shí)構(gòu)造類需要一個(gè)名字了,我們已經(jīng)控制了類的初始化狀態(tài)12 Sample a{str};13 {14 // Sample的構(gòu)造函數(shù)需要一個(gè)string的對象,但是編譯器推測出傳遞給Sample構(gòu)造函數(shù)的參數(shù)類型是字符串常量15 // 參數(shù)不匹配,但這還沒達(dá)到編譯失敗的條件,因?yàn)榫幾g器還沒檢查是否存在一種從字符串常量生成字符串對象的構(gòu)造函數(shù),16 // 答案是有的,string類提供了這樣的構(gòu)造函數(shù)17 // 接下來編譯器用字符串常量構(gòu)造出了string對象,自動(dòng)完成了string對象的創(chuàng)建18 // 并傳遞給Sample的構(gòu)造函數(shù),條件滿足,編譯順利完成19 Sample b{"b"};20 }21}22// 輸出23// Creating a Sample object:name = a24// Creating a Sample object:name = b25// Destoring a Sample object26// Destoring a Sample object
上面的例子有一個(gè)值得注意的地方,那就是對象b直接從字符串常量創(chuàng)建出來了,省略了中間字符串對象,其實(shí)這一步是編譯器為我們完成了,它的創(chuàng)建過程和a是完全一樣的,這種行為稱為隱式轉(zhuǎn)換。
這時(shí)的Sample類還是什么也做不了,甚至連哪一個(gè)對象被銷毀了我們都不知道。析構(gòu)函數(shù)是函數(shù),那么給析構(gòu)函數(shù)添加參數(shù)行不行呢?答案是不行,因?yàn)槲鰳?gòu)函數(shù)是編譯器自動(dòng)幫我們調(diào)用的,它不知道調(diào)用時(shí)需要什么參數(shù),所以就只能是無參。那么有什么辦法能正確標(biāo)記出是哪個(gè)對象被銷毀了呢,答案是成員變量。
成員變量和對象是同生共死的,它和對象使用同一塊內(nèi)存。對象創(chuàng)建就為成員變量也分配了空間,但是沒有初始化,需要開發(fā)者在構(gòu)造函數(shù)或者其他函數(shù)使用前初始化。在析構(gòu)函數(shù)調(diào)用時(shí),內(nèi)存尚未被回收,這時(shí)候是使用成員變量的最后時(shí)機(jī)。成員變量還有另一個(gè)重要的特點(diǎn),在類中定義的所有非static函數(shù)都能使用它,不需要通過函數(shù)參數(shù)傳遞。這也是類設(shè)計(jì)的初衷之一,用類管理數(shù)據(jù)。
所以,接下來的析構(gòu)函數(shù)可以這樣寫
1class Sample {
2private:
3 // 第一步,創(chuàng)建一個(gè)成員變量
4 std::string name;
5public:
6 // 第二步,在構(gòu)造函數(shù)中初始化成員變量
7 Sample(const std::string name) :name{ name } {
8 std::cout << "Creating a Sample object:name = " << name << std::endl;
9 }
10
11 ~Sample() {
12 //第三步,使用成員變量
13 std::cout << "Destoring a Sample object:name = " << name << std::endl;
14 }
15};
16
17int main() {
18 std::string str{ "a" };
19 Sample a{ str };
20 {
21 Sample b{ "b" };
22 }
23}
24// 輸出
25// Creating a Sample object:name = a
26// Creating a Sample object:name = b
27// Destoring a Sample object:name = b
28// Destoring a Sample object:name = a
可以看到,創(chuàng)建成員變量也很簡單,關(guān)鍵在于第二步,這和Java又不一樣。第二步中,初始化成員變量使用了特殊的語法,在構(gòu)造函數(shù)小括號后面添加了:,然后普通變量初始化的語法,稱之為成員變量初始化。這樣寫的關(guān)鍵原因在于,對象創(chuàng)建需要先申請內(nèi)存,內(nèi)存申請后使用:后面的初始化方式初始化成員變量,最后才調(diào)用構(gòu)造函數(shù)完成對象的創(chuàng)建,每一步都有它對應(yīng)的位置和作用。假如像Java一樣寫在構(gòu)造函數(shù)里面,就相當(dāng)于將第二步放到了第三步,打亂了它本來的順序。
為了說明成員函數(shù)確實(shí)在對象的整個(gè)生命周期都可以使用,我們再給它添加一個(gè)成員函數(shù)吧。
1class Sample{2 void print() {3 std::cout << "Invoke print name = " << name << std::endl;4 }5 //其余不變6}7int main() {8 std::string str{ "a" };9 Sample a{ str };10 {11 Sample b{ "b" };12 b.print();13 }14 a.print();15}16// 輸出17// Creating a Sample object:name = a18// Creating a Sample object:name = b19// Invoke print name = b20// Destoring a Sample object:name = b21// Invoke print name = a22// Destoring a Sample object:name = a
print它沒有參數(shù),但是它的函數(shù)體使用了成員變量name,可以看到,它也能正常工作。Sample,我們還可以提供一個(gè)無參的構(gòu)造函數(shù),然后將name初始化為空字符串,這樣print和析構(gòu)函數(shù)也能正常工作。數(shù)據(jù)的轉(zhuǎn)移和共享
數(shù)據(jù)拷貝
數(shù)據(jù)創(chuàng)建之后,不僅可以供成員函數(shù)使用,還可能被轉(zhuǎn)移到其他對象中去?;蛘吆推渌麑ο蠊蚕?。復(fù)制構(gòu)造函數(shù)可以控制數(shù)據(jù)以怎樣的方式和其他對象共享。
1class Sample {
2private:
3 int value;
4public:
5 Sample(const int value) :value{ value } {
6 std::cout << "Create value = " << value << std::endl;
7 }
8
9 // 以Sample命名,是構(gòu)造函數(shù),函數(shù)參數(shù)是自己的類型,說明是復(fù)制構(gòu)造函數(shù)
10 // 這個(gè)復(fù)制構(gòu)造函數(shù)選擇用賦值的形式共享value數(shù)據(jù)
11 Sample(const Sample& sample) :value{ sample.value } {
12 std::cout << "Copy create object" << std::endl;
13 }
14
15};
16
17void use(Sample sample) {
18 //函數(shù)返回,sample對象被銷毀
19}
20
21int main() {
22 Sample a{ 1 };
23 // a的數(shù)據(jù)被分享給一個(gè)臨時(shí)對象了,此時(shí)出現(xiàn)了兩個(gè)對象,它們的value都是1
24 use(a);
25}
26// 輸出
27// Create value = 1
28// Copy create object
復(fù)制構(gòu)造函數(shù)有以下幾個(gè)特征
會(huì)出現(xiàn)至少兩個(gè)同類型的對象。因?yàn)閺?fù)制需要先有一個(gè)存在的對象,再用這個(gè)存在的對象數(shù)據(jù)初始化另一個(gè)正在創(chuàng)建的對象的成員變量。這也是復(fù)制構(gòu)造函數(shù)參數(shù)是自己的原因。
存在變量從無到有初始化的情況都會(huì)調(diào)用復(fù)制構(gòu)造函數(shù)。函數(shù)調(diào)用,形參需要初始化為實(shí)參,參數(shù)本來不存在,調(diào)用函數(shù)會(huì)傳遞一個(gè)已存在的對象,就會(huì)調(diào)用到復(fù)制構(gòu)造函數(shù)。這也是為什么復(fù)制構(gòu)造函數(shù)參數(shù)是引用的類型。假如是普通變量,調(diào)用復(fù)制構(gòu)造的時(shí)候需要產(chǎn)生臨時(shí)變量,臨時(shí)變量又需要調(diào)用復(fù)制構(gòu)造函數(shù),程序就會(huì)陷入無限遞歸中。
除了函數(shù)調(diào)用,函數(shù)返回值,用對象初始化新變量的情況也會(huì)調(diào)用到復(fù)制構(gòu)造函數(shù)。函數(shù)返回后,函數(shù)體中所有的局部變量都會(huì)被銷毀,返回值也屬于一種局部變量肯定也要被銷毀,但是返回后的值卻需要被 外部使用,它們的生命周期是不一樣的,由此我們就知道肯定創(chuàng)建了一個(gè)新的對象,這個(gè)對象被局部返回值初始化,但是有著和外部一樣的生命周期。用對象初始化變量就更直觀了,初始化的對象是從無到有創(chuàng)建的。符合構(gòu)造函數(shù)出現(xiàn)的特點(diǎn)。
我們可以來驗(yàn)證一下
1//其余不變
2Sample returnSample() {
3 // 用普通構(gòu)造函數(shù)初始化的
4 Sample sample{ 2 };
5 return sample;
6}
7int main() {
8 Sample a{ 1 };
9 std::cout << "init local variable" << std::endl;
10 // b是新對象,用a初始化的,所以調(diào)用了復(fù)制構(gòu)造函數(shù)
11 Sample b = a;
12 // use的形參被用來初始化
13 std::cout << "Use Sample as parameter" << std::endl;
14 use(a);
15 //返回的sample被用來初始化c
16 std::cout << "return sample" << std::endl;
17 Sample c = returnSample();
18}
19// 輸出
20// Create value = 1
21// init local variable
22// Copy create object
23// Use Sample as parameter
24// Copy create object
25// return sample
26// Create value = 2
27// Copy create object
可以看到,這三種情況都會(huì)造成復(fù)制構(gòu)造函數(shù)的調(diào)用。
數(shù)據(jù)移動(dòng)
數(shù)據(jù)拷貝雖然簡單易行,但是還是有個(gè)小瑕疵??紤]下面這種場景:
1void swap(Object& left,Object& right){
2 // 有新對象產(chǎn)生,拷貝構(gòu)造,目前內(nèi)存中有兩份一模一樣的left
3 Object temp=left;
4 // 賦值操作,生成了一個(gè)right的臨時(shí)對象
5 left=right;
6 // 賦值操作,生成了一個(gè)temp的臨時(shí)對象
7 right=temp;
8 // 三個(gè)臨時(shí)對象都被銷毀
9}
10
11int main(){
12 Object a;
13 Object b;
14 swap(a,b);
15 return 0;
16}
一個(gè)簡單的交換邏輯,我們就生成了很多的臨時(shí)對象,假如我們操作的是列表,大對象,短時(shí)間內(nèi)大量創(chuàng)建并銷毀對象,就會(huì)造成內(nèi)存抖動(dòng),嚴(yán)重影響系統(tǒng)的穩(wěn)定性,而且,我們的真正目的只是將兩個(gè)變量的值交換一下而已。所以相較于拷貝,我們還有更好的選擇:移動(dòng)。
左值和右值
說起移動(dòng),就不得不提到左值和右值。這里的左和右是相對于=來說的。我們知道=是用來賦值的,這下面隱藏著三個(gè)動(dòng)作:生,取,寫。在內(nèi)存中生成一個(gè)臨時(shí)數(shù)據(jù),讀取變量保存位置,將臨時(shí)變量內(nèi)容寫入保存位置。生就是指的右值,它保存在我們不知道的內(nèi)存位置,在寫動(dòng)作完成后,它就被回收了。而取對應(yīng)的就是左值,我們用變量名保存了它的內(nèi)存位置,在它作用域內(nèi)可以反復(fù)讀寫。所以右值最大的特點(diǎn)就是不知道地址,如i=i+1就會(huì)先生成一個(gè)i+1的臨時(shí)對象,我們不知道地址,所以它是右值。與之相對的左值,是可以通過&讀到地址的。
接下來我們再來談一談引用。我們通常是用別名來理解引用的,但是可能會(huì)忽略一個(gè)小細(xì)節(jié),別名也是需要有歸屬的,也就是它代表的地址在哪里?;谶@個(gè)前提,我們就可以推導(dǎo)出凡是存在內(nèi)存中的數(shù)據(jù),都是有地址的,而右值是存在內(nèi)存中的,它也應(yīng)該需要一種方式來獲得地址,稱之為右值引用,相對的一般變量的引用就稱為左值引用。
說回到移動(dòng),前面的復(fù)制構(gòu)造函數(shù)雖然能將數(shù)據(jù)和其他對象共享,但是大部分情況下,數(shù)據(jù)其實(shí)不需要共享的,只需要轉(zhuǎn)移,也就是將數(shù)據(jù)的所有權(quán)移動(dòng)到另一個(gè)對象上,原始對象就不再有效。所以C++提供了移動(dòng)構(gòu)造函數(shù)來完成這個(gè)操作。
1class Sample {
2private:
3 int* value;
4
5public:
6 Sample(const int value) :value{ new int{value} } {
7 std::cout << "Create value = " << value << std::endl;
8 }
9
10 Sample(const Sample& sample) :value{ new int {*sample.value} } {
11 std::cout << "Copy create object" << std::endl;
12 }
13
14 Sample(Sample&& sample) :value{ sample.value } {
15 sample.value = nullptr;
16 std::cout << "Move create object" << std::endl;
17 }
18
19 ~Sample() {
20 delete value;
21 std::cout << "destory sample" << std::endl;
22 }
23
24 friend std::ostream& operator<<(std::ostream& os, const Sample& sample) {
25 os << "Sample value is " << sample.value;
26 return os;
27 }
28
29};
30
31void use(Sample sample) {
32 std::cout << "Use sample " << sample << std::endl;
33}
34
35
36int main() {
37 // 普通變量,1被使用后馬上銷毀了
38 int a = 1;
39 //左值引用
40 int& b = a;
41 //右值引用,引用的就是1那個(gè)暫存的地址
42 int&& c = 1;
43 //可以修改引用的值
44 c = 2;
45
46 Sample sample{ 1 };
47
48 use(std::move(sample));
49
50 std::cout << sample << std::endl;
51}
52// 輸出
53// Create value = 1
54// Move create object
55// Use sample Sample value is 009B8E90
56// destory sample
57// Sample value is 00000000
58// destory sample
在上面的代碼里,我們真正使用sample對象的是函數(shù)use,use執(zhí)行完后,sample就沒用了。所以我們用std::move將數(shù)據(jù)轉(zhuǎn)移到了函數(shù)實(shí)參中,外部的sample不再擁有那塊內(nèi)存的占用。很多場景其實(shí)都是類似的情況:外部配置參數(shù)后,傳遞給某個(gè)函數(shù)使用,所以這種情況下就沒必要構(gòu)造一個(gè)新的對象出來,假如業(yè)務(wù)很長的話,sample對象就會(huì)一直占用內(nèi)存,但是它是早就沒用了的。所以移動(dòng)構(gòu)造函數(shù)就發(fā)揮了大作用。
數(shù)據(jù)共享
除了通過復(fù)制構(gòu)造函數(shù)和成員函數(shù)共享數(shù)據(jù)外,還可以通過友元類和友元函數(shù)。它們都是一種特殊的訪問數(shù)據(jù)的形式,可以直接訪問到數(shù)據(jù),不經(jīng)過成員函數(shù)的調(diào)用。所以在有些時(shí)候友元能幫助減少函數(shù)調(diào)用的花銷,有些時(shí)候則會(huì)引入不可預(yù)期的行為。
1class FriendClass {
2
3public:
4 void useSample(const Sample& sample) {
5 std::cout << "Sample value is " << sample.value << std::endl;
6 }
7};
上面的例子,如果按照常規(guī)是無法通過編譯的,因?yàn)?code style="font-size: inherit;line-height: inherit;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0px 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">sample的value是私有的。前面我們知道,成員函數(shù)是可以訪問私有變量的,但是這個(gè)類是定義在Sample外的,這個(gè)函數(shù)是另一個(gè)類的成員函數(shù),完全沒辦法完成這種訪問。當(dāng)然,這種情況下,我們可以修改Sample類的定義,添加一個(gè)成員函數(shù)就解決了。但是假如FriendClass有多個(gè)成員函數(shù)都需要訪問Sample的私有成員呢,這個(gè)時(shí)候添加成員函數(shù)的方式就不再適用,所以出現(xiàn)了友元類。
實(shí)現(xiàn)友元類很簡單,簡單到只需要添加一條聲明。首先友元類需要至少兩個(gè)類,一個(gè)類是想要訪問私有成員的源類,另一個(gè)是含有私有成員的目標(biāo)類,然后我們把友元聲明放在目標(biāo)類里,源類就可以順利訪問到目標(biāo)類的私有成員了。在上面的例子FriendClass想要訪問Sample的私有成員,所以它是源類,是普普通通的類。Sample含有FeiendClass想訪問的私有成員value,所以它是目標(biāo)類,聲明需要添加到它的類定義里面。
1Class Sample{
2 private:
3 int value;
4 friend class FriendClass;
5
6 //其余不變
7}
加上這一條之后,前面的FriendClass就可以正常通過編譯了。這一句的威力很大,大到FriendClass的所有成員函數(shù)都能訪問到value。假如這不是你的期望,但是還是想要直接訪問到value,那么就可以適用友元函數(shù)。
友元函數(shù)是普通的函數(shù),雖然它聲明在類里,但是不能直接訪問到類的私有成員,而是通過函數(shù)參數(shù)的形式。為了和普通的成員函數(shù)區(qū)分開來,它的聲明最前面需要添加關(guān)鍵字friend。friend仿佛像打開了權(quán)限控制的開關(guān),可以使函數(shù)訪問到參數(shù)的私有成員。
1class Sample{
2 friend std::ostream& operator<<(std::ostream& os,const Sample& sample) {
3 os << "Sample value is " << sample.value << std::endl;
4 return os;
5 }
6 //其余不變
7}
8
9int main() {
10 Sample a{ 1 };
11 std::cout << a << std::endl;
12}
13
14// 輸出
15// Create value = 1
16// Sample value is 1
函數(shù)<<是友元函數(shù),因?yàn)楹瘮?shù)聲明有關(guān)鍵字friend。友元函數(shù)不是成員函數(shù),想在函數(shù)體訪問到成員變量,需要添加函數(shù)參數(shù)。那么函數(shù)參數(shù)有很多個(gè),怎樣確定參數(shù)私有成員的可訪問性呢,這就得看這個(gè)友元函數(shù)聲明在哪個(gè)類里面了,友元函數(shù)的聲明位置直接確定了它訪問私有成員的范圍。
特殊的成員函數(shù)
C++的類有極大的定制性,這種定制性不僅僅表現(xiàn)在數(shù)據(jù)上,還表現(xiàn)在成員函數(shù)上。我們知道一般的成員函數(shù)都是使用.來調(diào)用的,但是出于特殊的場景,有些情況下這種調(diào)用形式不僅僅不直觀,還效率不高。所以C++提出了運(yùn)算符的概念。之所以稱為運(yùn)算符,是因?yàn)楹瘮?shù)的調(diào)用和傳參形式和普通的成員函數(shù)不一樣。定義良好的運(yùn)算符可大大提高代碼的可讀性。如
[]操作符是下標(biāo)運(yùn)算符,有了它的幫助,我們就可以像obj[2]這樣取容器中的元素了。()則可以把對象當(dāng)成函數(shù)一樣直接調(diào)用,實(shí)現(xiàn)函數(shù)式編程的效果。->可以返回另外的對象,使得它可以表現(xiàn)出另一個(gè)對象的行為。
還有其他的諸如++,--等操作符,在定義特定類型的類時(shí),提供合適的運(yùn)算符函數(shù)能使我們的類更簡潔、好用。
總結(jié)
總的來說,類是一個(gè)數(shù)據(jù)管理器,構(gòu)造函數(shù)控制數(shù)據(jù)生成,來源可以是其他類型,也可以是相同類型。用相同類型生成新數(shù)據(jù)的時(shí)候,有復(fù)制和移動(dòng)兩種選擇。復(fù)制構(gòu)造函數(shù)控制相同類型的數(shù)據(jù)共享行為,其主要目標(biāo)就是實(shí)現(xiàn)兩個(gè)類型在構(gòu)造函數(shù)完成那一刻,在內(nèi)存中的數(shù)據(jù)是完全一致的。移動(dòng)構(gòu)造函數(shù)的目標(biāo)則是將現(xiàn)有的數(shù)據(jù)轉(zhuǎn)移到當(dāng)前構(gòu)造的對象上來,然后使現(xiàn)有的數(shù)據(jù)失效,從而達(dá)到減少對象創(chuàng)建、銷毀,增加內(nèi)存利用率的目的。除此之外,還能使用成員函數(shù)改變或者訪問數(shù)據(jù),最終在析構(gòu)函數(shù)中結(jié)束數(shù)據(jù)的生命。此外友元類或者友元函數(shù)也是一種數(shù)據(jù)訪問途徑。
具體類的主要矛盾是數(shù)據(jù),設(shè)計(jì)類的關(guān)鍵還是要弄清數(shù)據(jù)流向。數(shù)據(jù)自身在內(nèi)部能有什么狀態(tài),能實(shí)現(xiàn)什么行為,是成員函數(shù)該完成的工作。此外還要考慮相同類型相互構(gòu)造時(shí)數(shù)據(jù)的共享情況,是完全隔離,還是相互影響,這些都是應(yīng)該考慮的問題。畢竟確保數(shù)據(jù)從創(chuàng)建到銷毀的可靠性和有效性是一個(gè)具體類應(yīng)該完成的基本功能。
