面向對象的程序設計
面向對象編程(Object Oriented Programming,OOP,面向對象程序設計) 的主要思想是把構成問題的各個事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描敘一個事物在整個解決問題的步驟中的行為。
面向過程就是分析出解決問題所需要的步驟,然后用函數逐步實現,再依次調用就可以了。
面向對象和面向過程是兩種不同的編程思想,沒有哪一種絕對完美,要根據具體需求擬定開發方案。例如,開發一個小型軟件或應用程序,工程量小,短時間內即可完成,完全可以采用面向過程的開發方式,使用面向對象,反而會增加代碼量,降低開發效率。
面向過程的編程語言(如C語言)不能創建類和對象,不能用面向對象的思想來開發程序;面向對象的編程語言(如C++、PHP等)保留了面向過程的關鍵字和語句,仍然可以采用面向過程的思想來開發程序。
面向對象是面向過程的補充和完善。
注意,不要“死磕”概念上的理解,很多有經驗的程序員甚至都不能完全解釋清楚面向對象和面向過程的區別,要重在實踐,不斷揣摩編程語言的思想。
類和對象的基本概念
為了方便說明,我們將從現實生活中的實例入手。
我們知道,工業上使用的鑄件(電飯鍋內膽、汽車地盤、發動機機身等)都是由模子鑄造出來的,一個模子可以鑄造出很多相同的鑄件,不用的模子可以鑄造出不同的鑄件。這里的模子就是我們所說的“類”,鑄件就是我們所說的“對象”。
類,是創建對象的模板,一個類可以創建多個相同的對象;對象,是類的實例,是按照類的規則創建的。
屬性和方法
由模子鑄造出來的鑄件(對象),有很多參數(長度、寬度、高度等),能完成不同的操作(煮飯、承重、保護內部零件等)。這里的參數就是對象的“屬性”,完成的操作就是對象的“方法”。
屬性是一個變量,用來表示一個對象的特征,如顏色、大小、重量等;方法是一個函數,用來表示對象的操作,如奔跑、呼吸、跳躍等。
對象的屬性和方法統稱為對象的成員。
類的繼承
一個類(子類)可以繼承另一個類(父類)的特征,如同兒子繼承父親的DNA、性格和財產。
子類可以繼承父類的全部特征,也可以繼承一部分,由程序靈活控制。
C++面向對象程序設計舉例
這里我們將通過幾個簡單的例子來演示如何站在面向對象的角度設計程序,以及使用類的好處。
【例】最簡單的例子。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream> using namespace std; class Time //定義Time類 { public : //數據成員為公用的 int hour; int minute; int sec; }; int main( ) { Time t1; //定義t1為Time類對象 cin>>t1.hour; //輸入設定的時間 cin>>t1.minute; cin>>t1.sec; //輸出時間: cout<<t1.hour<< ":" <<t1.minute<< ":" <<t1.sec<<endl; return 0; } |
運行情況如下:
1
2
|
1232 43↙ 12:32:43 |
幾點注意:
1) 在引用數據成員hour,minute,sec時不要忘記在前面指定對象名。
2) 不要錯寫為類名,如寫成
Time.hour,Time.minute,Time.sec
是不對的。因為類是一種抽象的數據類型,并不是一個實體,也不占存儲空間,而對象是實際存在的實體,是占存儲空間的,其數據成員是有值的,可以被引用的。
3) 如果刪去主函數的3個輸入語句,即不向這些數據成員賦值,則它們的值是不可預知的。
【例】引用多個對象的成員。
1) 程序(a)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <iostream> using namespace std; class Time { public : int hour; int minute; int sec; }; int main( ) { Time t1; //定義對象t1 cin>>t1.hour; //向t1的數據成員輸入數據 cin>>t1.minute; cin>>t1.sec; cout<<t1.hour<< ":" <<t1.minute<< ":" <<t1.sec<<endl; //輸出t1中數據成員的值 Time t2; //定義對象t2 cin>>t2.hour; //向t2的數據成員輸入數據 cin>>t2.minute; cin>>t2.sec; cout<<t2.hour<< ":" <<t2.minute<< ":" <<t2.sec<<endl; //輸出t2中數據成員的值 return 0; } |
運行情況如下:
1
2
3
4
|
1032 43↙ 10:32:43 22 32 43↙ 22:32:43 |
程序是清晰易懂的,但是在主函數中對不同的對象一一寫出有關操作,會使程序冗長。為了解決這個問題,可以使用函數來進行輸入和輸出。見程序(b)。
2) 程序(b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <iostream> using namespace std; class Time { public : int hour; int minute; int sec; }; int main( ) { void set_time(Time&); //函數聲明 void show_time(Time&); //函數聲明 Time t1; //定義t1為Time類對象 set_time(t1); //調用set_time函數,向t1對象中的數據成員輸入數據 show_time(t1); //調用show_time函數,輸出t1對象中的數據 Time t2; //定義t2為Time類對象 set_time(t2); //調用set_time函數,向t2對象中的數據成員輸入數據 show_time(t2); //調用show_time函數,輸出t2對象中的數據 return 0; } void set_time(Time& t) //定義函數set_time,形參t是引用變量 { cin>>t.hour; //輸入設定的時間 cin>>t.minute; cin>>t.sec; } void show_time(Time& t) //定義函數show_time,形參t是引用變量 { cout<<t.hour<< ":" <<t.minute<< ":" <<t.sec<<endl; //輸出對象中的數據 } |
運行情況與程序(a)相同。
3) 程序(c)
可以對上面的程序作一些修改,數據成員的值不再由鍵盤輸入,而在調用函數時由實參給出,并在函數中使用默認參數。將程序(b)第8行以下部分改為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int main( ) { void set_time(Time&, int hour=0, int minute=0, int sec=0); //函數聲明 void show_time(Time&); //函數聲明 Time t1; set_time(t1,12,23,34); //通過實參傳遞時、分、秒的值 show_time(t1); Time t2; set_time(t2); //使用默認的時、分、秒的值 show_time(t2); return 0; } void set_time(Time& t, int hour, int minute, int sec) { t.hour=hour; t.minute=minute; t.sec=sec; } void show_time(Time& t) { cout<<t.hour<< ":" <<t.minute<< ":" <<t.sec<<endl; } |
程序運行時的輸出為:
1
2
|
12:23:34 (t1中的時、分、秒) 0:0:0 (t2中的時、分、秒) |
以上兩個程序中定義的類都只有數據成員,沒有成員函數,這顯然沒有體現出使用類的優越性。在下面的例子中,類體中就包含了成員函數。
【例】改用含成員函數的類來處理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#include <iostream> using namespace std; class Time { public : void set_time( ); //公用成員函數 void show_time( ); //公用成員函數 private : //數據成員為私有 int hour; int minute; int sec; }; int main( ) { Time t1; //定義對象t1 t1.set_time( ); //調用對象t1的成員函數set_time,向t1的數據成員輸入數據 t1.show_time( ); //調用對象t1的成員函數show_time,輸出t1的數據成員的值 Time t2; //定義對象t2 t2.set_time( ); //調用對象t2的成員函數set_time,向t2的數據成員輸入數據 t2.show_time( ); //調用對象t2的成員函數show_time,輸出t2的數據成員的值 return 0; } void Time::set_time( ) //在類外定義set_time函數 { cin>>hour; cin>>minute; cin>>sec; } void Time::show_time( ) //在類外定義show_time函數 { cout<< hour<< ":" << minute<< ":" << sec<< endl; } |
運行情況與例8.2中的程序(a)相同。
幾點注意:
在主函數中調用兩個成員函數時,應指明對象名(t1,t2)。表示調用的是哪一個對象的成員函數。
在類外定義函數時,應指明函數的作用域(如void Time∷set_time( ))。在成員函數引用本對象的數據成員時,只需直接寫數據成員名,這時C++系統會把它默認為本對象的數據成員。也可以顯式地寫出類名并使用域運算符。
應注意區分什么場合用域運算符“∷”,什么場合用成員運算符“.”,不要搞混。
【例】找出一個整型數組中的元素的最大值。這個問題可以不用類的方法來解決,現在用類來處理,讀者可以比較不同方法的特點。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#include <iostream> using namespace std; class Array_max //聲明類 { public : //以下3行為成員函數原型聲明 void set_value( ); //對數組元素設置值 void max_value( ); //找出數組中的最大元素 void show_value( ); //輸出最大值 private : int array[10]; //整型數組 int max; //max用來存放最大值 }; void Array_max::set_value( ) //成員函數定義,向數組元素輸入數值 { int i; for (i=0;i<10;i++) cin>> array[i]; } void Array_max::max_value( ) //成員函數定義,找數組元素中的最大值 { int i; max=array[0]; for (i=1;i<10;i++) if (array[i]> max) max=array[i]; } void Array_max::show_value( ) //成員函數定義,輸出最大值 { cout<< "max=" << max; } int main( ) { Array_max arrmax; //定義對象arrmax arrmax.set_value( ); //調用arrmax的set_value函數,向數組元素輸入數值 arrmax.max_value( ); //調用arrmax的max_value函數,找出數組元素中的最大值 arrmax.show_value( ); //調用arrmax的show_value函數,輸出數組元素中的最大值 return 0; } |
運行結果如下:
1
2
|
12 12 39 -34 17 134 045 -91 76↙ (輸入10個元素的值) max=134 (輸入10個元素中的最大值) |
請注意成員函數定義與調用成員函數的關系,定義成員函數只是設計了一組操作代碼,并未實際執行,只有在被調用時才真正地執行這一組操作。
可以看出: 主函數很簡單,語句很少,只是調用有關對象的成員函數,去完成相應的操作。在大多數情況下,主函數中甚至不出現控制結構(判斷結構和循環結構),而在成員函數中使用控制結構。在面向對象的程序設計中,最關鍵的工作是類的設計。所有的數據和對數據的操作都體現在類中。只要把類定義好,編寫程序的工作就顯得很簡單了。