关于c ++:关于受保护的成员函数和派生类访问的混淆

Confusion regarding protected member functions and derived class access

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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成员公开,这样他们才能 所有人都通过共享的界面互相交谈,他们都是从公众那里获得的。

我是否有一些用例,我不考虑允许访问这些受保护成员的地方会破坏受保护成员的性质吗?


近重复项:

  • 具有受保护字段的细微C ++继承错误
  • 我可以从派生类中的静态函数访问受保护的基类成员吗?
  • 父类中受保护的数据在子类中不可用?
  • 访问父级受保护的变量

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

我相信这是为了防止一个派生类与同级派生类的不变量混淆。考虑

1
2
3
4
5
6
7
8
9
10
11
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知道这些不变量。


如果编译器允许这样的事情,那么您可以轻松地破坏封装。考虑一下:

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

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

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

更具体地说,您可以编写一个"包装器",该包装器将对任何类禁用protected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#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;
}


好笑的故事。我刚收到比尼亚(Bjarne)关于这个问题的回复。这是他的回应。

考虑

1
2
3
4
5
6
7
8
9
class B : public base
{
public:
    // ...
};

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

这引起了细微的错误。这有效:

1
2
3
4
5
6
7
8
class A : public base
{
public:
    void fa(base* pb)
    {
        f();     // use your own base
    }
};