嵌套类和可见范围的继承

时间:2012-10-13 13:01:39

标签: c++ inheritance nested-class

请考虑以下代码:

namespace base {
class Base {
    protected:
        class Nested {
            protected:
                Base* base;

            public:
                Nested(Base* _base) : base( _base ){}

                virtual void test() {
                    base->foo();
                    /*
                     * hmm.. we can use protected methods
                     */
                    base->bar();
                }
        };

    protected:
        Nested* nested;

        void bar(){}

    public:
        void foo(){}

        virtual void test() {
            nested = new Nested(this);
            nested->test();
        }
};
};

namespace inherited {
class Base : public base::Base {
    public:
        Base()
            : base::Base() {}

    protected:
        class Nested : public base::Base::Nested {
            public:
                Nested( inherited::Base* base )
                    : base::Base::Nested( base ) {}

            public:
                virtual void test() {
                    base->foo();
                    /*
                     * hmm.. and now they are not accessible
                     */
                    // base->bar();
                }
        };

    public:
        virtual void test() {
            foo();
            bar();

            nested = new Nested(this);
            nested->test();
        }
};
};

我的问题是为什么我们可以从base::Base访问base::Base::Nested的受保护方法/属性,但无法访问inherited::Baseinherited::Base::Nested的相同方法/属性?< / p>

我唯一能想到的是base::Basebase::Base::Nested的一种全局范围,因此它们是可访问的。 inherited::Baseinherited::Base::Nested的一种全局范围base::Base的受保护成员无法访问。但是,公共继承不应该改变可见性的范围,并且我不清楚访问无能的原因。

2 个答案:

答案 0 :(得分:2)

问题似乎是存储指针的类型而不是访问权限。考虑一下:

class B {
protected:
  void bar();
};

class D : public B {
public:
  void call() {
    this->bar(); // works
    static_cast<B*>(this)->bar(); // does not work
  }
};

当您尝试通过bar呼叫时,情况类似 存储在基础中的指针。

你可以通过降级基础解决这个问题,但我强烈反对。

答案 1 :(得分:1)

§11.4/ 1对访问受保护成员有所说明(由我强调相关部分):

当非静态数据成员或非静态成员函数是其命名类的受保护成员时,将应用超出第11章中所述之外的其他访问检查(11.2)如前所述,访问a保护成员被授予,因为引用发生在朋友或某个类C的成员中。如果访问要形成指向成员的指针(5.3.1),则嵌套名称指定者应表示C或类从C派生。所有其他访问涉及(可能是隐式的)对象表达式(5.2.5)。 在这种情况下,对象表达式的类应为C或从C派生的类。

这不容易解释。值得庆幸的是,标准提供了一些示例来说明其含义,其中一个似乎正是您的情况(除非我在您的代码中误解了某些内容,这很可能):

(注意我已删除了示例中不相关的部分,并为简单起见重命名了一些元素。)

class B {
  protected:
  int i;
  static int j;
};

class D : public B {
  void mem(B*);
};


void D::mem(B* pb) {
  pb->i = 1;                // ill-formed
  i = 3;                    // OK (access through this)

  /* The following cases are not directly relevant: */
  B::i = 4;                 // OK (access through this, qualification ignored)
  int B::* pmi_B = &B::i;   // ill-formed
  int B::* pmi_B2 = &D::i;  // OK
  j = 5;                    // OK (because j refers to a static member)
  B::j = 6;                 // OK (because B::j refers to a static member)
}

换句话说,在

pb->i = 1;

成员i是通过pb找到的,指向基类的指针,因此命名类B。但是访问权限来自mem()DB的成员。 DD不同,也不是从B派生的(尽管i = 3 来自this),因此不允许访问。

但是在

D

通过{{1}}找到该成员,因此命名类为{{1}}并允许访问。