为什么编译:
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(Foo& fooBar)
{
fooBar.fooBase();
}
};
但这不是吗?
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(FooBase& fooBar)
{
fooBar.fooBase();
}
};
一方面,C为该类的所有实例授予对私有/受保护成员的访问权限,但另一方面,它不授予对所有子类实例的基类的受保护成员的访问权限 . 这看起来与我不一致 .
我已经测试了使用VC和ideone.com编译,并编译了第一个但不是第二个代码片段 .
7 回答
当
foo
收到FooBase
引用时,编译器不知道该参数是否是Foo
的后代,因此它必须假设它不是 .Foo
可以访问其他Foo
对象的继承受保护成员,而不是所有其他兄弟类 .考虑以下代码:
如果
Foo::foo
可以调用任意FooBase
后代的受保护成员,那么它可以调用FooSibling
的受保护方法,该方法与Foo
没有直接关系 . 这不是保护访问应该如何工作 .如果
Foo
需要访问所有FooBase
对象的受保护成员,而不仅仅是那些也被称为Foo
后代的对象,则Foo
需要成为FooBase
的朋友:C++ FAQ很好地总结了这个问题:
关键是
protected
授予您访问您自己的成员副本的权限,而不是任何其他对象中的成员 . 这是一种常见的误解,因为我们通常会概括和陈述protected
授予对派生类型成员的访问权限(没有明确说明只有他们自己的基础......)现在,这是有原因的,并且通常您不应该访问层次结构的不同分支中的成员,因为您可能会破坏其他对象所依赖的不变量 . 考虑对某些大型数据成员(受保护)执行昂贵计算的类型,以及根据不同策略缓存结果的两种派生类型:
cache_on_read
类型捕获对数据的修改并将结果标记为无效,以便下次读取值重新计算 . 如果写入次数相对较高,这是一种很好的方法,因为我们只按需执行计算(即多次修改不会触发重新计算) .cache_on_write
预先计算结果,如果写入次数很少,这可能是一个很好的策略,并且您需要读取的确定性成本(想想读取的低延迟) .现在,回到原来的问题 . 两种缓存策略都保持一组比基数更严格的不变量 . 在第一种情况下,额外不变量是
cached
仅在data
在上次读取后未被修改时才为true
. 在第二种情况下,额外不变量是result_value
始终是操作的值 .如果第三个派生类型引用
base
并访问data
来写(如果protected
允许它),那么它将与派生类型的不变量相冲突 .话虽如此,语言的规范被打破(个人意见),因为它留下了后门来实现特定的结果 . 特别是,如果从派生类型的基础创建指向成员成员的指针,则在
derived
中检查访问,但返回的指针是指向base
成员的指针,该指针可应用于任何base
对象:在两个示例中,
Foo
都继承了受保护的方法fooBase
. 但是,在第一个示例中,您尝试从同一个类(Foo::foo
调用Foo::fooBase
)访问给定的受保护方法,而在第二个示例中,您尝试从另一个未被声明为友元类的类访问受保护的方法(Foo::foo
尝试调用FooBase::fooBase
,失败,后者受保护) .在第一个示例中,您传递了一个Foo类型的对象,它显然继承了fooBase()方法,因此可以调用它 . 在第二个示例中,您试图调用受保护的函数,只是这样,无论在哪个上下文中,您都无法从其声明的类实例中调用受保护的函数 . 在第一个示例中,您继承了受保护的方法fooBase,因此您有权将其称为WITHIN Foo上下文
我倾向于从概念的角度看问题和消息 . 如果您的FooBase方法实际上被称为"SendMessage"且Foo是"EnglishSpeakingPerson"且FooBase是SpeakingPerson,则您的受保护声明旨在将SendMessage限制在EnglishSpeakingPersons(和子类之间,例如:AmericanEnglishSpeakingPerson,AustralianEnglishSpeakingPerson) . 源自SpeakingPerson的另一种类型FrenchSpeakingPerson将无法接收SendMessage,除非您将FrenchSpeakingPerson声明为朋友,其中'friend'意味着FrenchSpeakingPerson具有从EnglishSpeakingPerson接收SendMessage的特殊能力(即可以理解英语) .
除了hobo's answer,您可以寻求解决方法 .
如果您希望子类想要调用
fooBase
方法,可以将其设为static
. 具有所有参数的子类可以访问静态受保护的方法 .