如何防止基 class 的受保护成员在子 class 的第二层仍然被访问?

How do you prevent protected members of the base class from still being accessed in the second level of the subclass?

一个例子来说明我的问题:

class Base
{
protected:
    int x{ 0 };
};

class DerivedClass1 : public Base
{
};

class DerivedClass2 : public DerivedClass1
{
public:
    int getX() const
    {
        return x;
    }
};

int main()
{
    DerivedClass2 d2{};
    int x{ d2.getX() };
    return 0;
}

我可以在 DerivedClass2 class 中访问 Base 的受保护成员,尽管 Base 的受保护成员只能在 DerivedClass1 中更改.通过将Base中的变量继承到DerivedClass1中,将形成一个class,DerivedClass2不得对其进行操作。在 C# 中,这可以通过 private protected 关键字实现,但如何在 C++ 中处理它?

您可以将数据成员声明为 private 并使用 friend 声明明确指定哪个 类 可以访问它:

class Base
{
    friend class DerivedClass1;
private:
    int x = 0;
};

class DerivedClass1 : public Base
{
    void test() {
        cout << x;  // OK: DerivedClass1 is a friend of Base
    }
};

class DerivedClass2 : public DerivedClass1
{
public:
    int getX() const
    {
        return x;  // ERROR: x is a private member
    }
};

受保护的后果是什么?

protected 的理念就是允许所有派生的 classes 访问这些成员。

虽然这是一个非常灵活的语言特性,但它在封装中造成了严重的弱点,这会鼓励打破 Liskov Substitution Principle(更准确地说是历史约束,因为派生的 class 可能会改变基础对象的状态而不经过基础 class 原语)。

如何避免它的弊端?

如果你想要更强的封装和限制访问,你需要去private。这确保只能使用 public 接口访问基 class 的内部状态。 (请注意,虽然它确保了历史约束,但它本身并不能保证 LSP)。结果是派生 class 无法访问。不是一开始推导,也不是后来。

你需要私人保护吗?

你想要的,是一种介于两者之间的:弱封装,但不是太弱。这在 C++ 中不存在。而且我不确定它会加强你的设计。

但如果您在特殊情况下需要此限制,则可以使用名称查找来解决:

class DerivedClass1 : public Base
{
private:
    using Base::x; 
// In DerivedClass1 you can still use x.
};

// But it will fail to compile in Derived2

Online demo

但我个人不建议走这条路。它很容易出错(您可能会忘记派生的一个兄弟中的 using )。编译器错误消息可能具有误导性。不管怎样,private 会产生更健壮的设计。

继承一个'private'基数:

class DerivedClass1 : private Base
{

};

编辑:通过public公开public或Base中的受保护成员或DerivedClass1中的受保护成员。 DerivedClass1 可以完全控制继承自 DerivedClass1[= 的 类 可以访问和不可以访问的 Base 成员23=].

这是否是一个好的解决方案取决于 Base 的复杂性。