假设我有这段代码:
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,但它并不完全相同:我正在尝试从派生类中访问受保护的成员。
答案 0 :(得分: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
成员的值。那不好。
答案 1 :(得分:3)
protected
表示可以在派生类中将作为成员进行访问。 不授予派生类无限制访问权。
推理(我推测)是这样派生类可以修改基类型以维护派生类型自己的契约。但是,它不需要访问其他派生类的受保护成员,因为这可能会使他们的合同无效。
合同是对成员国家的承诺。您可能熟悉的一些示例合同位于字符串的内部:size
包含缓冲区中字符串的长度,buffer[size]
包含null(这里有大量的技术细节,但它们“不重要”。此外,缓冲区始终指向null或具有唯一所有权的有效以null结尾的字符串。字符串类很难确保无论如何,所有这些都是真的。 (字符串实际上没有任何这些合同,因为它的成员是私有的,这只是一个例子)
答案 2 :(得分: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
}