关于受保护成员函数和派生 class 访问的混淆

Confusion regarding protected member functions and derived class access

我正在与一位同事讨论为什么以下内容无法在 Visual Studio 2008 中编译:

class base
{
protected:
    virtual void f(){}
};

class a : public base
{
public:
    void fa(base* pb)
    {
        pb->f(); // error C2248: 'base::f' : cannot access protected member declared in class 'base'
    }
};

他认为这是完全合理的,但我认为这是一个奇怪的限制,如果我想让 base 及其所有派生的 类 成为一个封闭的系统,我仍然需要做一些base 的成员 public,因此他们都可以通过共享接口相互交谈,他们都派生自 publicly。

是否有一些我没有想到的用例允许访问这些受保护成员会破坏受保护成员的性质?

如果编译器允许这样的事情,那么你可以很容易地破坏封装。想想这个:

base b;
a foo;
foo.fa(b); // we can now easily access/modify protected elements of `b`

在这种情况下,派生对象 foo 和基础 b 之间没有任何关系,但是您可以使用派生来访问它的 "guts"。这应该是不可能的(至少恕我直言)。

只需在 a.fa() 中执行 f() 即可,因为您只需修改 a 的基础部分,而不是一些不相关的对象。

更具体地说,您可以编写一个 "wrapper",它将禁用任何 class:

protected
#include <iostream>

class Base
{
public: // protected in your case, public here so it compiles
    int x{42};
public:
    int getx() {return x;}
};

template<typename T> // wrapper
class DisableProtected: public T
{
public:
    void modify(Base* b)
    {
        b->x = 24;
    }
};

int main()
{
    Base base;
    std::cout << base.getx() << std::endl;
    DisableProtected<Base> foo; 

    foo.modify(&base); // can modify any Base
    std::cout << base.getx() << std::endl;
}

接近重复:

  • subtle C++ inheritance error with protected fields
  • Can I access a base classes protected members from a static function in a derived class?
  • Protected data in parent class not available in child class?
  • Accessing parent's protected variables

Is there some use case I'm not thinking of where allowing access to these protected members could break the nature of protected members?

我相信这是为了防止派生 class 与兄弟派生 class 的不变量发生冲突。考虑

class A {
    protected: void foo();
};
class B : public A {
    // complicated code
};
class C : public A {
    void bar(B* b) {
        b->foo();
    }
};

这实际上允许 C 仅修改 BA 子对象,这可能会违反 B 对其 A 子对象强加的不变量, C 不可能知道。

有趣的故事。我刚收到 Bjarne 关于这个问题的回复。这是他的回应。

考虑

class B : public base
{
public:
    // ...
};

A a;
B b;
a.f(&b); // a manipulated b's base

这导致了细微的错误。这有效:

class A : public base
{
public:
    void fa(base* pb)
    {
        f();     // use your own base
    }
};