受C ++保护:无法从派生类中访问受基础保护的成员 [英] C++ protected: fail to access base's protected member from within derived class

查看:67
本文介绍了受C ++保护:无法从派生类中访问受基础保护的成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

诚然,这个问题的标题听起来与您的邻居Mike反复询问的问题几乎完全相同.我发现很多问题的措辞都是相同的,但我的问题都没有.

Admittedly, this question title sounds pretty much exactly the same as the question you neighbour Mike has repeatedly asked. I found quite a few questions worded the same way, but none was what my question is about.

首先,我想澄清这个问题的背景:

First of all, I'd like to clarify a few points for the context of this question:

1,c ++访问控制基于类而不是实例.因此,以下代码是完全有效的.

1, c++ access control works on a class basis rather than instance basis. Therefore, the following code is completely valid.

class Base
{
protected:
    int b_;

public:
    bool IsEqual(const Base& another) const
    {
        return another.b_ == b_; // access another instance's protected member
    }
};

2,我完全理解为什么以下代码无效-另一个可以是同级实例.

2, I completely understand why the following code is NOT valid - another can be a sibling instance.

class Derived : public Base
{
public:
    // to correct the problem, change the Base& to Derived&
    bool IsEqual_Another(const Base& another) const
    {
        return another.b_ == b_;
    }
};

现在该卸载我的真实问题了:

Now time to unload my real question:

假设在Derived类中,我有一个Base实例数组.如此有效,Derived是一个Base(IS-A关系),而Derived由Base(复合关系)组成.我从某个地方读到,这(指的是IS-A和Has-A的设计)是一种设计异味,而我一开始绝对不应该有这样的情况.好吧,例如,分形的数学概念可以通过IS-A和Has-A关系建模.但是,让我们暂时忽略对设计的意见,只关注技术问题.

Assume in the Derived class, I have an array of Base instances. So effectively, Derived IS A Base(IS-A relation), and Derived consists of Base(Composite relation). I read from somewhere that this(refers to the design of both IS-A and Has-A) is a design smell and I should never have a scenario like this in the first place. Well, the mathematical concept of Fractals, for example, can be modelled by both IS-A and Has-A relations. However, let's disregard the opinion on design for a moment and just focus on the technical problem.

class Derived : public Base
{
protected:
    Base base_;

public:
    bool IsEqual_Another(const Derived& another) const
    {
        return another.b_ == b_;
    }

    void TestFunc()
    {
        int b = base_.b_; // fail here
    }
};

该错误消息已经非常清楚地表明了该错误,因此您无需在答案中重复该错误:

The error message has already stated the error very clearly, so there's no need to repeat that in your answer:

Main.cpp:140:7:错误:"int Base :: b_"受到保护int b_;^Main.cpp:162:22:错误:在此上下文中int b = base_.b _;

Main.cpp:140:7: error: ‘int Base::b_’ is protected int b_; ^ Main.cpp:162:22: error: within this context int b = base_.b_;

确实,根据以下两个事实,上面的代码应该可以工作:

Really, according to the following 2 facts, the code above should work:

1,C ++访问控制基于类而不是实例(因此,请不要说我只能访问Derived的b_;我不能访问独立的Base实例的受保护成员-它是基于类的)

1, C++ access control works on class basis rather than instance basis(therefore, please don't say that I can only access Derived's b_; I can't access a stand alone Base instance's protected members - it's on class basis).

2,错误消息显示在此上下文中"-上下文是Derived(我试图从Derived内部访问基本实例的受保护成员.这是受保护成员的全部功能-应该可以从以下位置访问它在Base中或从Base派生的任何东西.

2, Error message says "within this context" - the context is Derived(I was trying to access a Base instance's protected member from within Derived. It's the very feature of a protected member - it should be able to be accessed from within Base or anything that derives from Base.

那为什么编译器会给我这个错误?

So why is the compiler giving me this error?

推荐答案

我只是将自己的评论变成答案,因为我发现这个问题很有趣.特别是在以下最小示例中, D 不会使我感到困惑:

I am just turning my comments into an answer because I find the issue interesting. In particular that in the following minimal example D doesn't compile baffled me:

class B            { protected: int i;          };
class D : public B { int f(B &b){ return b.i; } };

毕竟, D B ,并且应该能够完成 B 可以做的所有事情(访问除外)B 的私人成员),不是吗?

After all, a D is a B and should be able to do all that a B can do (except access B's private members), shouldn't it?

显然,C ++和C#的语言设计师都觉得太宽容了.埃里克·利珀特(Eric Lippert)

Apparently, the language designers of both C++ and C# found that too lenient. Eric Lippert commented one of his own blog posts saying

但这并不是我们选择的有趣或有价值的保护措施.兄弟"类彼此之间不会友好,因为否则保护就很少.

But that’s not the kind of protection we’ve chosen as interesting or valuable. "Sibling" classes do not get to be friendly with each other because otherwise protection is very little protection.


因为对11.4中提出的实际规则似乎有些困惑,所以我将对其进行解析,并通过一个简短的示例来说明基本思想.


Because there seems to be some confusion about the actual rule laid forth in 11.4 I'll parse it and illustrate the basic idea with a short example.

  1. 列出了本节的目的及其适用的内容(非静态成员).

  1. The purpose of the section is laid out, and what it applies to (non-static members).

除了第11条中所述的检查之外的其他访问检查当非静态数据成员或非静态成员函数时应用是其命名类别(11.2)的受保护成员

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2)

下面的示例中的命名类是 B .

The naming class in the example below is B.

上下文是通过总结到目前为止的章节而建立的(该章定义了受保护成员的访问规则).此外,还引入了"C类"的名称:我们的代码应该位于C的成员或朋友函数中,即具有C的访问权限.

Context is established by summarising the chapter so far (it defined access rules for protected members). Additionally a name for a "class C" is introduced: Our code is supposed to reside inside a member or friend function of C, i.e. has C's access rights.

如前所述,对受保护成员的访问是授予,因为引用发生在某些朋友或成员中 C类.

在下面的示例中,

"C类"也是 C 类.

现在仅定义实际检查.第一部分处理指向成员的指针,在这里我们将其忽略.第二部分涉及您每天访问对象的成员,该对象在逻辑上"涉及(可能是隐式的)对象表达式" .
这只是最后一句话,描述了整个部分的附加检查":

Only now the actual check is defined. The first part deals with pointers to members, which we ignore here. The second part concerns your everyday accessing a member of an object, which logically "involve a (possibly implicit) object expression".
It's just the last sentence which describes the "additional check" this whole section was for:

在这种情况下,对象表达式的类[通过哪个成员访问-pas]应为C或派生自C的类.

In this case, the class of the object expression [through which the member is accessed -pas] shall be C or a class derived from C.

对象表达式"可以是变量之类的东西,函数的返回值或取消引用的指针.对象表达式的类"是编译时属性,而不是运行时属性;通过一个访问而相同的对象可能会被拒绝或授予,具体取决于有关用于访问成员的表达式的类型.

The "object expression" can be things like a variable, a return value of a function, or a dereferenced pointer. The "class of the object expression" is a compile time property, not a run time property; access through one and the same object may be denied or granted depending on the type of the expression used to access the member.

此代码段演示了这一点.

This code snippet demonstrates that.

class B { protected: int b; };

class C: public B 
{
    void f()
    {
        // Ok. The expression of *this is C (C has an
        // inherited member b which is accessible 
        // because it is not declared private in its
        // naming class B).
        this->b = 1;    

        B *pb = this;

        // Not ok -- the compile time 
        // type of the expression *pb is B.
        // It is not "C or a class derived from C"
        // as mandated by 11.4 in the 2011 standard.
        pb->b = 1;
    }
};


我最初想知道这个规则,并假设以下理由:


I initially wondered about this rule and assume the following rationale:

眼前的问题是数据所有权和权限.

The issue at hand is data ownership and authority.

在没有代码 inside B 的情况下,明确提供访问权限(通过使 C 成为朋友或通过Alf的静态访问器之类的东西),除此以外没有其他类谁拥有"数据被允许访问它.这可以通过简单地定义同级并通过新的和未知的同级来修改原始派生类的对象来防止非法访问类的受保护成员.在这种情况下,Stroustrup在TCPPL中谈到了细微的错误".

Without code inside B explicitly providing access (by making C a friend or by something like Alf's static accessor) no other classes except those who "own" the data are allowed to access it. This prevents gaining illicit access to the protected members of a class by simply defining a sibling and modifying objects of the original derived class through the new and before unknown sibling. Stroustrup speaks of "subtle errors" in this context in the TCPPL.

虽然可以安全地从派生类的代码访问原始基类的对象(不同),但该规则仅涉及 expressions (编译时属性),而不涉及对象(运行时属性).尽管静态代码分析可能表明某种 Base 类型的表达式实际上从未引用同级,但是甚至没有尝试过这种操作,类似于有关别名的规则.(也许这就是Alf在他的帖子中的意思.)

While it would be safe to access (different) objects of the original base class from a derived class' code, the rule is simply concerned with expressions (a compile time property) and not objects (a run time property). While static code analysis may show that an expression of some type Base actually never refers to a sibling, this is not even attempted, similar to the rules concerning aliasing. (Maybe that is what Alf meant in his post.)

我想下面的基本设计原则是:保证对数据的所有权和权威性使一个类可以保证它可以维护与数据相关的不变性(在更改受保护的 a 之后,总会更改 b ).提供通过兄弟姐妹更改受保护属性的可能性可能会破坏不变量-兄弟姐妹不知道其兄弟姐妹的实现选择的详细信息(可能已写在很远的星系中).一个简单的示例是具有受保护的 width height 数据成员以及琐碎的公共虚拟访问器的 Tetragon 基类.从中得到了两个兄弟姐妹,平行四边形 Square .覆盖 Square 的访问器,使其始终也设置另一个维度,以保留正方形的等长边不变式,或者它们仅使用两者之一.现在,如果平行四边形可以直接通过 Tertragon 设置 Square width height code>引用,它们会打破不变性.

I imagine the underlying design principle is the following: Guaranteeing ownership and authority over data gives a class the guarantee that it can maintain invariants related to the data ("after changing protected a always also change b"). Providing the possibility to change a protected property from by a sibling may break the invariant -- a sibling does not know the details of its sibling's implementation choices (which may have been written in a galaxy far, far away). A simple example would be a Tetragon base class with protected width and height data members plus trivial public virtual accessors. Two siblings derive from it, Parallelogram and Square. Square's accessors are overridden to always also set the other dimension in order to preserve a square's invariant of equally long sides, or they only just use one of the two. Now if a Parallelogram could set a Square's width or height directly through a Tertragon reference they would break that invariant.

这篇关于受C ++保护:无法从派生类中访问受基础保护的成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆