为什么这编译:
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 ++允许访问该类的所有实例私有/ protected成员,但在另一方面,它不授予访问某个子类的所有实例的基类的保护成员。 这看起来相当不一致的给我。
我已经测试用VC和与ideone.com编译和编译两者在第一而不是第二代码段。
当foo
收到FooBase
参考,编译器不知道这个说法是否后裔Foo
,所以它必须假设它不是。 Foo
先后获得其他的继承保护成员Foo
对象 ,而不是其他的兄弟姐妹类。
考虑以下代码:
class FooSibling: public FooBase { };
FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?
如果Foo::foo
可调用任意的保护成员FooBase
后代,那么它可以调用protected方法FooSibling
,其中有没有直接的关系Foo
。 这是受保护的访问是如何工作的。
如果Foo
需要所有受保护的成员访问FooBase
对象,不只是那些也被称为是Foo
后代,然后Foo
需要成为朋友FooBase
:
class FooBase
{
protected:
void fooBase(void);
friend class Foo;
};
在C ++ FAQ很好地总结了这个问题:
[你]被允许选择自己的口袋里,但你不能来接你父亲的口袋里,也没有你哥哥的腰包。
关键的一点是, protected
您有权使用自己的成员的副本,而不是那些成员的任何其他对象。 这是一个普遍的误解,随着越来越多的往往不是我们概括和国家protected
访问权限授予成员在派生类(但没有明确指出,只是为了自己的基地......)
现在,这是有原因的,一般你不应该访问该成员在层次结构的不同分支,因为你可能会打破这样,其他目标所依赖的不变量。 考虑,其对一些大的数据成员(受保护的)和两个派生类型高速缓存结果以下不同的策略昂贵的计算类型:
class base {
protected:
LargeData data;
// ...
public:
virtual int result() const; // expensive calculation
virtual void modify(); // modifies data
};
class cache_on_read : base {
private:
mutable bool cached;
mutable int cache_value;
// ...
virtual int result() const {
if (cached) return cache_value;
cache_value = base::result();
cached = true;
}
virtual void modify() {
cached = false;
base::modify();
}
};
class cache_on_write : base {
int result_value;
virtual int result() const {
return result_value;
}
virtual void modify() {
base::modify();
result_value = base::result();
}
};
所述cache_on_read
型捕获修改对数据和标记结果为无效,使得该值的下一个读重新计算。 这是一个很好的方法,如果写入次数是比较高的,因为我们只执行按需计算(即多次修改不会触发重新计算)。 该cache_on_write
预先计算的结果前期,这可能是一个很好的策略,如果写入次数少,你想为读确定性成本(想想上读取低延迟)。
现在,回到原来的问题。 两种缓存策略保持严格不变集比基础的。 在第一种情况下,额外不变的是, cached
是true
只有当data
还未最后一次读取后修改。 在第二种情况下,额外不变的是, result_value
是运行在任何时候都值。
如果第三派生类型走上一参考base
和访问data
写入(如果protected
允许它),那么它会与派生类型的不变量断裂。
话虽这么说,因为它留下了后门,以实现特定的结果,语言规范被打破 (个人意见)。 特别地,如果创建一个指针从在派生类型的底座的构件的构件,则访问被在检查derived
,但返回的指针是指向的构件base
,其可被应用到任何 base
对象:
class base {
protected:
int x;
};
struct derived : base {
static void modify( base& b ) {
// b.x = 5; // error!
b.*(&derived::x) = 5; // allowed ?!?!?!
}
}
在这两个例子中Foo
继承的保护方法fooBase
。 然而,在你的第一个例子中,你尝试从同一类访问给定的保护方法( Foo::foo
调用Foo::fooBase
),而在第二个例子中,你尝试从未声明的另一个类访问受保护的方法作为朋友类( Foo::foo
尝试拨打FooBase::fooBase
,它失败了,后来被保护)。
在第一个例子传递Foo类型,这显然继承方法fooBase()的目的,并且因此能够调用它。 在第二个例子中,你正试图调用一个受保护的功能,只需所以,无论在哪个方面,你不能调用从那里其宣称的这么一个类的实例保护功能。 在第一个例子,你继承的保护方法fooBase,所以你必须把它WITHIN美孚方面的权利
我倾向于看到的概念和信息方面的事情。 如果您FooBase方法实际上是所谓的“SendMessage函数”和Foo是“EnglishSpeakingPerson”和FooBase是SpeakingPerson, 受保护的声明意在限制SendMessage函数EnglishSpeakingPersons之间(和子类如:AmericanEnglishSpeakingPerson,AustralianEnglishSpeakingPerson)。 从SpeakingPerson衍生另一种类型FrenchSpeakingPerson将无法接收SendMessage函数,除非你宣布FrenchSpeakingPerson作为一个朋友,在“朋友”指的是FrenchSpeakingPerson必须从EnglishSpeakingPerson收到的SendMessage(即能听懂英语)的特殊能力。
除了流浪汉的答案 ,你可能会寻求一个解决方法。
如果你想在子类要调用的fooBase
方法你可以把它static
。 静态保护的方法是通过与所有参数的子类访问。