C++ 類當中為什麼要有private?
不清楚為什麼要人為添加條件限制訪問,這只是編譯階段的限制,編譯之後不存在這個限制。為什麼不都用public?
面向對象編程語言一般具三大特徵:封裝(encapsulation)、繼承(inheritance)、多態(polymorphism)。
C++ 的 private 用於封裝,限制成員變數及函數的可見性。
許多語言功能都是用於在編譯期加諸限制,避免誤用,也便於長期維護。
有一天,你老闆給你一個任務:給我們正在開發的系統寫一個小功能吧,功能是:輸入一個人的編號(例如學號之類的),返回這個人的個人信息。允許新增信息條目。
你想:這還不簡單。
你寫了一個類,這個類長成這樣(Info中包含人員的編號):class InfoStorage {
public:
std::vector&
}
每次用戶輸入編號number,就對public的data進行一次線性搜索,如果找到符合條件的Info返回。新增成員就是一次push_back。
你把這個類以QQ的形式交給老闆。過了一段時間,大家向你提了個意見:為啥不把查找,添加信息的功能放到函數里呢?每次都操作data成員太麻煩。
你想:這還不簡單。於是,你修改了一下InfoStorage類:class InfoStorage {
public:
std::vector&
Info query(const std::string number);
void add(const Info info);
}
你把這個類再次以QQ的形式交給老闆,在這之後,大家全都開心地用起了query,add函數(以前直接操作data的那些代碼就留著吧,寫都寫了)。
一個月後,老闆在電梯里遇上你,和你閑聊,說:
「 今天天氣不錯。」,老闆說,「 對了,現在我們的人員信息都要放進伺服器的資料庫里,你把那個查找功能改改吧。」你覺得天氣頓時不好了。兩天後,你終於改好了InfoStorage類:class InfoStorage {
public:
DatabaseConnection connection;
// ........
// ........
Info query(const std::string number);
void add(const Info info);
}
結果出事了。
出事是顯然的,你想,因為在query,add函數出現以前,大量的代碼都直接用data來操作數據嘛,把這些地方都改成query,add函數的調用不就行了。可是事情沒有這麼簡單:同事王某(參加過編程競賽培訓),嫌query太慢,自己寫了個二分查找,當然,在查找前,他將data按照編號大小,重新排了個序。「線性查找太慢!」,王某說。同事李某(熱愛生活,屬羊),寫了個函數:BinaryData serialize(const std::vector&
「老闆說了,要加個保存數據的功能。」
........
你崩潰了。
同事們紛紛安慰你:「你又沒說不能這麼用。」這個時候,一個老頭拍了拍你的肩膀,說:聊聊?你們進行了如下對話:老頭:「數據是什麼?」
你:「數據就是內存里的bytes!」老頭:「那麼,我有兩個數,x和y,我說他們表達一個複數(complex number),x是實部,y是虛部。」你:「x還可以表達長度,y表達角度呢,我學過高中數學的,複數有兩種表達方式。」老頭:「x和y在內存里是固定的bytes,但是根據我們各自的解釋,它們所代表的意義可以多種多樣,假設x是實部,那麼我們對複數取實部時,返回x的值即可,假設x是長度,那麼複數的實部,就是 x * cos(y) 。現在,我再問你,數據是什麼?」你想:雖然x和y的含義可以多種多樣,但是我們只要正確定義了 real() 和 imag() 操作來取實部和虛部,那麼無論我如何表示一個複數,用戶都不會受影響.....啊對了,我必須禁止用戶直接操作x和y,因為離開了 real() 和 imag(),它們沒有任何意義。你:「......數據就是....定義在它們之上的操作!無論InfoStorage數據具體是如何存儲的,可能是在資料庫,在文件,在內存中.... 它的概念是始終如一的!它是一個能夠 add 和 query 的存在,」老頭笑了。
你醒了,腦海里浮現出 private 這個單詞,又浮現出了另一個令你感到陌生的片語:「data abstraction」 (關於數據的抽象)
在電梯里,你遇見了老闆:「給我們正在開發的系統寫一個小功能吧!」說對了,就是為了讓編譯器檢查出來。
靜態語言的設計思路,很多時候就是為了讓編譯器儘可能多的檢查出錯誤。
private 也是一樣。
考慮一下,變數名本身也是只在編譯階段有意義的。簡單的說:1. 不懂別瞎動。2. 我不保證不會改
你以為一個屬性就是一個屬性?我後面隱藏了一大堆機制呢,比如合法值檢查,比如關聯屬性的關聯修改,比如屬性的變化事件通知,甚至並發訪問的臨界區。
你買個手機電視機洗衣機也打開外殼動裡面的電器開關,電位器,機器裝置嗎?我給你提供什麼按鈕你就怎麼用,動壞了你負責?
你用個類就像用個IC一樣,只要引腳和上面的電氣特性一樣,你管我裡面是用與非門還是或非門實現的?今天我有這個屬性,明天我可能不要這個屬性了也能實現那些功能,你依賴我內部實現就玩不轉了。它本身是封裝思想的體現,作為一門面向對象語言,這個特性表明了面向對象的思想。一個對象內部不應當成為public,用戶只能調用它的方法。迫使用戶使用公共的介面、方法,更改代碼時,用戶調用類的代碼只需要少量或不需要修改。這叫模塊化(?)另外,類不一定是寫給自己用的……即使是寫給自己,也應該做好private和public的區分,以確保不會無意中破壞。覺得不清楚的,可以和C、java對比一下
因為你不會想希望別人用你的模塊的時候使用那些底層的實現,而只想他們使用你暴露的介面,這是一個君子協定
君子協定,在不使用pimpl模式的情況下(物理封裝),命名也沒有加什麼internal前綴或者impl後綴(警示語封裝),提醒使用者不要去使用這些成員(編譯封裝)。
擋不住用戶#define private public 或者通過指針偏移的方法訪問哈。
就是簡單地防止自己或者別人手賤而已。真的想隱藏好,需要把實際內容也藏起來,徹底阻止手賤慾望的產生。
C語言(GNU的很多庫都是這樣的):
/* 在頭文件里 */
typedef struct _MyClass MyClass;
MyClass* my_class_new();
void my_class_ref(MyClass* self);
void my_class_unref(MyClass* self);
void my_class_method1(MyClass* self, float param1, int param2);
void my_class_method2(MyClass* self, double param1, char param2);
/* 在實現文件里 */
struct _MyClass {
int member1;
float member2;
};
各個函數的定義。。。。。。
C++的對等寫法(Qt很多部分是這樣的):
// 在頭文件里
class MyClass
{
public:
MyClass();
virtual ~MyClass();
method1(float param1, int param2);
method2(double param1, char param2);
private:
struct Guts;
Guts* guts = nullptr;
};
// 在實現文件里
struct MyClass::Guts
{
int member1;
float member2;
}
各個函數的實現。。。。。。
為了一天你發現自己寫的類爛的跟shi一樣的時候,可以修改類的內部實現(就是重構),而不用改使用這個類的代碼。
防止你寫錯程序,和const一個意思。
防君子不防小人。其實編譯之前知道偏移用指針強轉也能讀。
編譯期檢測&>運行時檢測&>不檢測。
private是為了編譯期檢測。
沒做運行時檢測是另一個問題,是為了性能而省略了這個事兒,讓程序員自己避免。
private是面向對象里很重要的部分啊!這樣可以封裝一個類,你不需要知道內部的原理,你也不能干涉內部的東西,你只需要使用public的介面便可以。就像買來一個手機,你不需要知道它是如何編碼和發送無線電波的,你只需要知道怎麼用它就行。如果private暴漏出來就好像把怎樣編碼啊,怎樣和基站通信啊混同正常的使用方式這些都放你面前讓你來選擇如何去使用這個手機,那得多麻煩。
其實我也覺得除非是模板類 其他時候都不需要private
可訪問性的邊界應該在模塊和模塊之間
C++可以把一個庫視作一個模塊
C++作為介面提供語言的時候
對外公開的介面的頭文件中應該全部是所有函數都是純虛函數的類
類似於C#和Java中的介面
然後通過一個靜態函數創建實現了該介面的具體內部實現
內部實現的時候 因為都可以視作在同一個模塊內部
不需要加上這些訪問修飾符來增加麻煩
統一全部為public
對Java我也是同樣的建議
Java同樣也沒有模塊
只能把相同包名的當做一個模塊
對於內部實現一律採用包訪問許可權
為了不用人工檢查介面許可權吧,心累
類是有隱私的,父母不能訪問,而朋友可以。
其實設計的初衷是不想讓其他類訪問,免得讓使用者產生不必要的干擾。
為什麼有Private和Public之分,這不是個技術問題,更不是C++技術問題,而是一個工程化和業務問題,什麼東西不願讓外部看見,什麼東西希望讓外部知道。
只是OOP的設計思路,充分考慮到工程管理和業務場景的需求了而已。
我認為有多態和繼承,是可以不用private的,既然private是不讓人訪問,就乾脆不讓人感知到,比如COM介面,只有介面申明,有什麼數據成員,內部如何使用 完全不需要關注。
C++ primer 第五版 242頁
推薦閱讀:
※怎麼用好《C++ Primer》(英文版)?
※C++11 移動構造函數問題?
※常量字元串是右值,為什麼沒有調用相應的右值重載函數?
※你有什麼關於Linux下C++並行編程的好書和經驗跟大家分享?
※為什麼 C++ 中,基類指針可以指向派生類對象?