2015-04-03 128 views
11

比方說,我有這樣的代碼:爲什麼我無法從基類的實例訪問受保護的成員?

class foo{ 
    protected: 
    int a; 
}; 

class bar : public foo { 
    public: 
    void copy_a_from_foo(foo& o){ 
    a = o.a; // Error 
    } 
    void copy_a_from_bar(bar& o){ 
    a = o.a; // OK 
    } 
}; 

int main(){ 
    bar x; 
    foo y; 
    bar z; 
    x.copy_a_from_foo(y); 
    x.copy_a_from_bar(z); 
} 

這裏class bar沒有問題,從同一類的其他實例訪問受保護的成員a,但是當我嘗試做相同的基類foo的實例,編譯器給我一個錯誤,說a受保護。該標準對此有何評論?

誤差

prog.cpp: In member function 'void bar::copy_a_from_foo(foo&)': 
prog.cpp:3:7: error: 'int foo::a' is protected 
    int a; 
    ^
prog.cpp:9:11: error: within this context 
    a = o.a; 

P.S:我在this question一看,但它不太一樣:我想從派生類中訪問受保護的成員。

+1

同http://stackoverflow.com/questions/3060572/c-protected-pointer-member-to -r-same-class-and-access-privileges?rq = 1 – 2015-04-03 21:37:58

+0

由於問題與編譯器錯誤*本身無關,因此刪除了編輯,更多關於規則背後的基本原理「無法訪問受保護的成員基類「 – 2015-04-03 21:39:39

+0

@ EugeneK no。我知道我無法從基類訪問受保護的成員。我的問題是爲什麼我不能。 – 2015-04-03 21:44:24

回答

6

只能通過指針或對派生類型的對象的引用來訪問基類的protected成員。

如果更改

void copy_a_from_bar(bar& o){ 
    a = o.a; 
} 

void copy_a_from_bar(bar& o){ 
    foo& foo_ref = o; 
    a = o.a;  // OK. Accessing it through `bar&` 
    a = foo_ref.a; // Not OK. Accessing it through `foo&` 
} 

,你會看到相同的錯誤。

This SO answer給出了爲什麼允許訪問基類的protected成員可能違反基類成員的protected狀態的原因。

假設你有:

class baz : public foo 
{ 
    void update_a(foo& f) 
    { 
     f.a = 20; 
    } 
}; 

及用途:

bar x; 
baz z; 
z.update_a(x); 

如果被允許,baz就能改變bar成員的值。這是不好的。

+0

你知道這種行爲背後的理性嗎? – MikeMB 2015-04-03 21:38:44

+1

我不知道......你基本上是在將問題轉化爲答案......我對背後的原因更感興趣,爲什麼會這樣。 – 2015-04-03 21:40:25

+1

確保* only *派生類可以訪問基類的受保護數據。 – haavee 2015-04-03 21:47:45

3

protected表示可以在派生類中作爲成員訪問。它確實不是授予派生類不受限制的訪問。

推理(我假設)是這樣派生類可以修改基類型以維護派生類型自己的合約。但是,它不需要訪問其他派生類的受保護成員,因爲這可能會使合同無效。

合同是對成員國的承諾。您可能熟悉的一些示例契約位於字符串的內部:size包含緩衝區中字符串的長度,而buffer[size]包含空值(這裏有很多技術細節,但它們並不重要)。此外,緩衝區始終指向null或有效的以null結尾的字符串,並擁有唯一的所有權。字符串類努力確保,無論如何,所有這些事情都是真實的。(字符串實際上沒有任何這些合同,因爲它的成員是私人的,這只是一個例子)

2

這是一個常見的誤解,因爲protected意味着什麼。這並不意味着您可以從派生類型訪問基類型的任何對象的成員,而只能訪問屬於派生類型的對象的子對象。

基本原理是,你可以控制你的對象的成員,你知道你在做什麼,但不會輕易搞亂其他對象的狀態。考慮下面這個例子,其中CachedDoubleValue維持緩存的值與值的基礎對象的雙:

class Base { 
protected: 
    int x; 
}; 
class CachedDoubleValue : public Base { 
    int y; 
public: 
    void set(int value) { 
     x = value; 
     y = 2 * x; 
    } 
}; 
class AnotherDerived : public Base { 
public: 
    static void setX(Base &b, int value) { 
     b.x = 10; 
    } 
}; 
int main() { 
    CachedDoubleValue a; 
    a.set(1);    // a.x = 1, a.y = 2 
    AnotherDerived::modifyX(a, 10); 
     // Invariant broken: a.x = 10, a.y = 2; a.x != 2 * a.y 
} 
相關問題