C++ 類存取控制public/private/protected探討

來源:互聯網
上載者:User
摘要:本文是從飲水思源BBS C/C++版上一篇文章引發的思考。當時看到文章,突然覺得平時見慣了的,在這裡似乎變得陌生了,究竟存取控制符怎樣起作用,怎樣使用,該怎樣理解,本文試圖給出討論。

原帖如下 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修飾)。例如上面的例子class P : protected O {…}; 那麼某個函數通過類P訪問O中成員時,該函數對類O中的public成員只有protected許可權。

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

class A
{
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;
}

由於這種技術用到了強制類型轉換,當謹慎使用。

題目:自己定義一個class,它只有一個資料成員,且為private, 其成員函數都為public, 它產生兩個對象obj1, obj2, 先要求用最少的成員函數實現obj1訪問obj2的private資料成員。

實現一:

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.