Kai*_*udi 35 c++ inheritance encapsulation protected
为什么编译:
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(Foo& fooBar)
{
fooBar.fooBase();
}
};
Run Code Online (Sandbox Code Playgroud)
但这不是吗?
class FooBase
{
protected:
void fooBase(void);
};
class Foo : public FooBase
{
public:
void foo(FooBase& fooBar)
{
fooBar.fooBase();
}
};
Run Code Online (Sandbox Code Playgroud)
一方面,C++为该类的所有实例授予对私有/受保护成员的访问权限,但另一方面,它不授予对所有子类实例的基类的受保护成员的访问权限.这看起来与我不一致.
我已经使用VC++和ideone.com测试了编译,并且编译了第一个但不是第二个代码片段.
Rob*_*edy 30
当foo
收到FooBase
引用时,编译器不知道参数是否是后代Foo
,所以它必须假设它不是.Foo
可以访问其他Foo
对象的继承受保护成员,而不是所有其他兄弟类.
考虑以下代码:
class FooSibling: public FooBase { };
FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?
Run Code Online (Sandbox Code Playgroud)
如果Foo::foo
可以调用任意FooBase
后代的受保护成员,那么它可以调用与之FooSibling
无直接关系的受保护方法Foo
.这不是保护访问应该如何工作.
如果Foo
需要访问所有FooBase
对象的受保护成员,而不仅仅是那些也被称为Foo
后代的对象,则Foo
需要成为以下的朋友FooBase
:
class FooBase
{
protected:
void fooBase(void);
friend class Foo;
};
Run Code Online (Sandbox Code Playgroud)
Dav*_*eas 10
关键是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();
}
};
Run Code Online (Sandbox Code Playgroud)
该cache_on_read
类型捕获对数据的修改并将结果标记为无效,以便下次读取值重新计算.如果写入次数相对较高,这是一种很好的方法,因为我们只按需执行计算(即多次修改不会触发重新计算).在cache_on_write
预先计算的结果前期,这可能是一个很好的策略,如果写入次数少,你想为读确定性成本(想想上读取低延迟).
现在,回到原来的问题.两种缓存策略都保持一组比基数更严格的不变量.在第一种情况下,额外不变的是,cached
是true
只有在data
还没有被最后一次读取后修改.在第二种情况下,额外不变量就是result_value
操作的值始终.
如果第三个派生类型对a进行了引用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 ?!?!?!
}
}
Run Code Online (Sandbox Code Playgroud)