C++ 類訪問控制public/private/protected探討

C++ 類訪問控制public/private/protected探討

原帖如下

 1 #include <IOSTREAM>
 2 using namespace std;
 3 
 4 class A{
 5 public:
 6     A(int i_,int j_)
 7     {
 8         i=i_;
 9         j=j_;
10     }
11     void disp(A &a)
12     {
13         cout<<a.i<<endl<<a.j<<endl;
14     }
15 
16 private:
17     int i;
18 protected:
19     int j;
20 };
21 
22 int main(int argc, char* argv[])
23 {
24     A a(123,456);
25     A b(789,543);
26     a.disp(b);
27     b.disp(a);
28 
29     return 0;
30 }

 

初看起來,倒是會產生疑問。爲什麼會這樣,是否有bug?
仔細考究起來,我們其實可以這樣看待類和對象:
類是將數據成員和進行於其上的一系列操作(成員函數)封裝在一起,注意:成員函數可以操作數據成員(可以稱類中的數據成員爲泛數據成員)!
對象是類的實例化,怎樣理解實例化?其實每一個實例對象都只是對其中的數據成員初始化,內存映像中每個對象僅僅保留屬於自己的那份數據成員副本。而成員函數對於整個類而言卻是共享的,即一個類只保留一份成員函數。
那麼每個對象怎樣和這些可以認爲是“分離”的成員函數發生聯繫,即成員函數如何操作對象的數據成員?記住this指針,無論對象通過(.)操作或者 (->)操作調用成員函數,編譯時刻,編譯器都會將這種調用轉換成我們常見的全局函數的形式,並且多出一個參數(一般這個參數放在第一個),然後將this指針傳入這個參數。於是就完成了對象與成員函數的綁定(或聯繫).
實例化後就得到同一個類的多個不同的對象,既然成員函數共享的,那麼成員函數就可以操作對象的數據成員。
問題是現在有多個對象,成員函數需要知道操作的是哪個對象的數據成員?
比如有對象obj1和obj2,都屬於A類,A類有public成員函數foo()
如果obj1調用該函數,編譯時會給foo函數傳入this指針,obj1,foo中操作obj1自身的成員就不用任何修飾,直接訪問,因爲其中的數據成員自動根據this指針找到。
如果obj1調用該函數,同樣可以訪問同類的其他對象的數據成員!那麼你需要做的是讓foo函數知道是同類對象中哪個對象的數據成員,一個解決辦法是傳入同類其他對象的指針或引用,那麼就可以操作同類其他對象的數據成員。
foo(A &obj)
這樣定義,然後調用:
obj1.foo(obj2)
就可以在obj1訪問obj2的數據成員,而無論這些數據成員是private還是protected

搬出C++Object Model,可以畫出各個對象的內存map就可以更清晰的看出:

總結:C++的訪問修飾符的作用是以類爲單位,而不是以對象爲單位。

通俗的講,同類的對象間可以“互相訪問”對方的數據成員,只不過訪問途徑不是直接訪問.
步驟是:通過一個對象調用其public成員函數,此成員函數可以訪問到自己的或者同類其他對象的public/private/protected數據成員和成員函數(類的所有對象共用),而且還需要指明是哪個對象的數據成員(調用函數的對象自己的成員不用指明,因爲有this指針;其他對象的數據成員可以通過引用或指針間接指明)

C++public,protected,private訪問小結
第一:private,public,protected方法的訪問範圍.(public繼承下)
private: 只能由該類中的函數、其友元函數訪問,不能被任何其他訪問,該類的對象也不能訪問.
protected: 可以被該類中的函數、子類的函數、以及其友元函數訪問,但不能被該類的對象訪問
public: 可以被該類中的函數、子類的函數、其友元函數訪問,也可以由該類的對象訪問
注:友元函數包括兩種:設爲友元的全局函數,設爲友元類中的成員函數

第二:類的繼承後方法屬性變化:
使用private繼承,父類的所有方法在子類中變爲private;
使用protected繼承,父類的protected和public方法在子類中變爲protected,private方法不變;
使用public繼承,父類中的方法屬性不發生改變; 
 

 

public:

protected:

private:

public繼承

public

protected

---

protected繼承

protected

protected

---

private繼承

private

private

---


protected繼承和private繼承能降低訪問權限

再次提到:可以提供訪問行爲的主語爲“函數”。
類體內的訪問沒有訪問限制一說,即private函數可以訪問public/protected/private成員函數或數據成員,同理,protected函數,public函數也可以任意訪問該類體中定義的成員
public繼承下,基類中的public和protected成員繼承爲該子類的public和protected成員(成員函數或數據成員),然後訪問仍然按類內的無限制訪問

對於類域範圍內,成員函數訪問無所謂訪問限制。
對於繼承情況下的基類private成員,在派生類中仍然繼承了下來,只不過它不能直接訪問,   即使在類裏也不行,   更不用說是類對象了。
可以通過下列例子看到:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class C{
 5 public:
 6     C(int val) : m_c(val) {}
 7 private:
 8     int m_c;
 9 };
10 
11 class D: public C{
12 public:
13     D(int val1, int val2) : C(val1), m_d(val2) {}
14     int m_d;
15 };
16 
17 int main()
18 {
19 
20 
21     cout << sizeof(C) << " " << sizeof(D) << endl; // 4  8
22     D obj(2, 25);
23     cout << &obj << " " << &obj.m_d << endl; //0x0012FF78   0X0012FF7C
24     //cout << obj.m_c;  //error, 不能訪問
25 
26     D *ptr = &obj;
27     int *iptr = (int *)ptr;
28     cout << *iptr << " " << *(iptr+1) << endl;//2  25
29     
30     return 0;
31 }
32 
33 


同樣,加入虛函數,可以看到VC編譯器將vptr放置在數據區的最開頭

 

下面這個問題摘自論壇的一個帖子

已知3個類O、P和Q,類O中定義了一個私有方法F1、一個公有方法F2和一個受保護的方法F3:類P和類Q是類O的派生類,其繼承方式如下所示:
class P : protected O {…};
class Q : public O {…};
關於方法F1的描述中正確的是___(34)___;關於方法F2韻描述中正確的是___(35)___;關於方法F3的描述中正確的是___(36)___。
(34)
A.方法F1無法被訪問
B.只有在類O內才能訪問方法F1
C.只有在類P內才能訪問方法F1
D.只有在類Q內才能訪問方法F1
(35)
A.類O、P和Q的對象都可以訪問方法F2
B.類P和Q的對象都可以訪問方法F2
C.類0和Q的對象都可以訪問方法F2
 D.只有在類P內才能訪問方法F2
(36)A.類0、P和Q的對象都可以訪問方法F3
B.類0、P和Q的對象都不可以訪問方法F3
C.類0和Q的對象都可以訪問方法F3
D.類P和Q的對象都可以訪問方法F3。

有甚麼辦法可以簡單地記住這許多的規則? 下文告訴你一個根本不需要記的辦法。

顧名思義,private/public/protected分別表示 私有/公開/保護,它們是一組用於訪問權限控制的關鍵字。那麼首先,需要澄清的一個關鍵點是,是要控制誰訪問誰的權限?這個訪問的主語(施事)是誰?賓語(受事)是誰?

我們經常聽到這樣的說法:

1)一個類友元可以訪問該類的任何成員(包括成員變量及成員方法,下同)。
2)private成員只有該類自身可以訪問,protected成員只有該類及其派生類可以訪問,public成員所有的人都可以訪問。

賓語(受事)是誰這一點很明確,是類的成員(包括成員變量及成員方法)。主語(施事)是誰?這是讓大家發生混淆的關鍵點。也是這個說法中含糊不清的地方。

想清楚一點,其實主語(施事)指的是一個函數,而不是(當然更不是變量)。private/public/protected要控制的是一個函數(施事)對一個類的成員(包括成員變量及成員方法)的訪問權限。因此比較完整的說法是:

1)一個類友元(包含友元函數或者友元類的所有成員函數)可以訪問該類的任何成員(包括成員變量及成員方法)。

2)除去友元外,private成員只有該類自身的成員函數可以訪問,protected成員只有該類的成員函數及其派生類的成員函數可以訪問,public成員則所有的函數都可以訪問。

也就是說,當我們說一個類可以訪問XXX,其實暗指這個類的成員函數可以訪問XXX。瞭解了這一點,外加一條顯而易見的規則,上面的問題就不難回答了。這條規則是:

3)派生類在繼承時可削弱成員的訪問權限(通過protected/private修飾)。例如上面的例子classP : protected O {…}; 那麼某個函數通過類P訪問O中成員時,該函數對類O中的public成員只有protected權限。

補充:有一種技術叫Member Spy(類成員間諜),通過該技術派生類可將基類的protected成員修改爲public權限。這種技術用到了using關鍵字。舉例如下:

classA
{
protected:
 int m_data;
};

class SpyA: public A
{
public:
 using A::m_data;
};

void TestSpy(A* pA)
{
 SpyA* pSpyA = static_cast<SpyA*>(pA);
 // 強制轉換A爲SpyA,這要求SpyA沒有成員變量且沒有重載A中的虛函數。
 // 現在你可以通過pSpyA訪問m_data了。例如:int data = pSpyA->m_data;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章