在類的定義中,可以用 private、public 和 protected 三種關鍵字來指定成員可被存取的範圍。
private:用來指定私有成員。一個類的私有成員,不論是成員變數還是成員函數,都只能在該類的成員函數內部才能被存取。
public:用來指定公有成員。一個類的公有成員在任何地方都可以被存取。
protected:用來指定保護成員。這需要等介紹“繼承”之後再解釋。
三種關鍵字出現的次數和先後次序都沒有限制。成員變數的可存取範圍由離它前面最近的那個存取範圍說明符決定。
如果某個成員前面沒有存取範圍說明符,則對 class 來說,該成員預設地被認為是私有成員;對 struct 來說,該成員預設地被認為是公有成員。例如:
class A
{
int m, n;
public:
int a, b;
int func1();
private:
int c, d;
void func2();
public:
char e;
int f;
int func3();
};
在上面的類 A 中,成員 a、b 和 func1 是公有的,c、d 和 func2 是私有的,e、f 和 func3 又是公有的。m 和 n 沒有指定可存取範圍,則是私有的。如果把 class 換成 struct,那麼 m 和 n 就是公有的。
下面的程式可以說明公有成員和私有成員的區別。假設一個企業員工管理程式的一小部分程式碼如下:
#include <iostream>
#include <cstring>
using namespace std;
class CEmployee {
private:
char szName[30]; //名字
public:
int salary; //工資
void setName(char* name);
void getName(char* name);
void averageSalary(CEmployee el, CEmployee e2);
};
void CEmployee::setName(char* name) {
strcpy(szName, name); //ok
}
void CEmployee::getName(char* name) {
strcpy(name, szName); //ok
}
void CEmployee::averageSalary(CEmployee el, CEmployee e2)
{
salary = (el.salary + e2.salary) / 2;
}
int main()
{
CEmployee e;
strcpy(e.szName, "Tom1234567889"); //編譯出錯,不能存取私有成員
e.setName("Tom"); //ok
e.salary = 5000; //ok
return 0;
}
在上面的程式中,szName 是私有成員,其他成員都是公有的。
私有成員只能在成員函數內部存取,因此第 14 行和第 17 行沒有問題,這兩條語句都是在存取函數所作用的那個物件的 szName 私有成員。另外,類的成員函數內部可以存取任何同類物件的私有成員。
所謂成員函數內部,指的就是成員函數的函數體內部。main 函數中的語句,如第 26 行,當然就不在 CEmployee 的成員函數內部,因此該行試圖存取 e 這個物件的 szName 私有成員變數就會導致編譯錯誤。
而第 27 行雖然也不屬於 CEmployee 類的成員函數內部,但其存取的是物件 e 的公有成員 setName,因此沒有問題。同理,第 28 行也沒有問題。
在 CEmployee 類的成員函數外面,若要存取 CEmployee 物件的 szName 私有成員變數,不能直接存取,只能通過兩個成員函數 setName 和 getName 間接進行存取。
“隱藏”的作用
設定私有成員的機制叫作“隱藏”。“隱藏”的一個目的就是強制對成員變數的存取一定要通過成員函數進行。這樣做的好處是,如果以後修改了成員變數的型別等屬性,只需要更改成員函數即可;否則,所有直接存取成員變數的語句都需要修改。
以上面的企業員工管理程式為例,如果 szName 不是私有的,那麼整個程式中可能會有很多類似於第 26 行
strcpy(man1, szName, "Tom1234567889");
這樣的語句。假設需要將該程式移植到記憶體空間緊張的手持裝置上,希望將 CEmployee 類的成員變數 szName 改為 char szName[5],以便節約空間,那麼所有這樣的語句都要找出來檢查一番並修改,以防止陣列越界。這顯然很麻煩。
如果將 szName 變為私有的,則除了 CEmployee 類的成員函數內部,其他地方不可能出現第 26 行那樣對 szName 直接存取的語句,所有對 szName 的存取都是通過成員函數進行的。例如:
e.setName("Tom12345678909887");
就算 szName 的長度變短了,上面的語句也不需要修改,只要修改 setName 成員函數,在其中去掉超長的部分,確保陣列不越界就可以了。
可見,“隱藏”有利於程式的修改。
“隱藏”機制還可以避免對物件的不正確操作。有的成員函數只是設計用來讓同類的成員函數呼叫的,並不希望對外開放,因此就可以將它們宣告為私有的,隱藏起來。
現代軟體開發絕大多數是合作完成的,一個程式設計師設計了一個類,可能被許多程式設計師使用。在設計類的時候,應當盡可能隱藏使用者不需要知道的實現細節,只留下必要的介面(即一些成員函數)來對物件進行操作,這樣能夠避免類的使用者隨意使用成員函數和成員變數而導致錯誤。
就像數位照相機的設計者會用外殼將內部的電路全部封裝隱藏起來,使用者不需要知道數位照相機的具體工作原理以及其中有哪些器件,只要能通過設計者留下的介面,即外殼上的各種按鈕來使用照相機即可。如果把內部的電路、器件、開關都暴露給使用者,那麼外行使用者很可能會把照相機弄壞。