【问题标题】:why this definition of a derived class is illegal?为什么派生类的这个定义是非法的?
【发布时间】:2018-08-10 03:45:44
【问题描述】:

为什么派生类 Derived_from_Private 是非法的? 我注意到成员函数引用了 Base,但为什么它不能引用 Base 类?

class Base {
public:
  void pub_mem(); // public member
protected:
  int prot_mem; // protected member
private:
  char priv_mem; // private member
};

struct Pub_Derv : public Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Priv_Derv : private Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Prot_Derv : protected Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Public : public Pub_Derv {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Private : public Priv_Derv {
  // illegal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Protected : public Prot_Derv {
  // legal
  void memfcn(Base &b) { b = *this; }
};

【问题讨论】:

  • 你对protected和private这两个关键字的理解是什么?
  • 私有继承没有隐式转换到基类(你必须使用强制转换)

标签: c++ class c++11 inheritance


【解决方案1】:

类的名称作为公共成员插入到自身的范围内。这就是所谓的injected-class-name。在派生类Derived_from_Private 中查找Base 的名称将找到其注入类名称,而不是正常名称。由于Base的注入类名被视为Base的公共成员,因此被视为Priv_Derv的私有编号,因此在Derived_from_Private中不可访问。

引用自[class.access.spec] paragraph 5

[ 注意: 在派生类中,基类名称的查找将找到注入的类名称,而不是声明范围内的基类名称。在声明它的范围内,注入的类名可能比基类的名称更难访问。 — 尾注 ] [ 示例:

class A { };
class B : private A { };
class C : public B {
  A* p;             // error: injected-class-name A is inaccessible
  ::A* q;           // OK
};

— 结束示例 ]

【讨论】:

    【解决方案2】:

    继承可以提供subtyping结构扩展

    当您从基类私有继承时,您没有子类型,只有结构扩展。然后(在你有问题的情况下)当你写 b = *this alas *thisnot 类型 Base 因为你使用了它的私有继承。

    通常使用私有继承(这并不意味着它是一个好习惯)来轻松构建非常简单的组合(有一个基础,但不是是一个基础) .

    【讨论】:

    • 其实只要我用一个Base作为参数。编译器报错。
    • 什么意思?
    • struct Derived_from_Private : public Priv_Derv { // 非法 void memfcn(Base &b) { } };这也是违法的!!!!为什么???
    • 在这种情况下编译器认为你在​​谈论私有基类Base 然后抱怨,如果你想谈论在全局范围内定义的类型,请写::Base
    【解决方案3】:

    Priv_Derv 私有继承 Base。这意味着只有类本身知道它也是Base,并且只有Priv_Derv 的成员函数可以使用Base 的成员。

    您可以稍后让 Derived_from_PrivatePriv_Derv 公开继承。这是合法的。但不幸的是,由于以前的私有继承,好像Derived_from_Private 没有Base 作为基类。

    因此你的成员函数将无法编译:

        void memfcn(Base &b) { b = *this; }
    

    *thisDerived_from_Private,但将其转换为 Base 类是非法的,因为由于私有继承,与该类没有已知关系。

    【讨论】:

    • 但我没有使用 Base 的成员。即使我没有将 *this 分配给 Base,只要我有 Base 的参数(引用或副本),编译器就会给我一个错误。
    • @JingleiRuan 有趣!似乎私有继承会影响非限定名称查找。使用 ::Base 而不是 Base 来显式引用正确的(这里是全局的)命名空间,它将编译。
    • @JingleiRuan 是的,就是这样!根据stackoverflow.com/a/41595470/3723423,这似乎是一个编译器错误但是这个编译器问题并不影响我最初答案的有效性。您会看到 :: 解决方法将使用空主体进行编译,但一旦您尝试分配就会失败。
    【解决方案4】:

    表达式

    b = *this;
    

    需要调用从*thisBase 类型的左值的隐式转换,以便调用隐式声明的Base::operator=(const Base&)。此转换通过路径Derived_from_Private -> Priv_Derv -> Base。由于Priv_DervBase 作为私有基地,Derived_from_Private 无权访问第二个链接。

    【讨论】:

    • 与作业无关。只要我将 Base 作为参数传递,编译器就会给我一个错误。
    • @JingleiRuan 啊,原来如此。 xskxzr 提供的答案是正确的。但是您可以使用::Base 来解决它。
    猜你喜欢
    • 2013-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多