为什么这个派生类的定义是非法的?

3
为什么派生类 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; }
};

1
你对保护关键字和私有关键字的理解是什么?在什么情况下使用它们? - KeithSmith
使用私有继承时,没有到基类的隐式转换(必须使用强制转换)。 - M.M
4个回答

3
表达式
b = *this;

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

这与作业无关。只要我将Base作为参数传递,编译器就会给出错误提示。 - Jing
@JingleiRuan 啊,没错。xskxzr提供的答案是正确的。但你可以通过使用::Base来解决它。 - Brian Bi

2
Priv_Derv 继承自 Base,但是继承方式为私有。这意味着只有类本身知道它也是一个 Base,只有 Priv_Derv 的成员函数可以使用 Base 的成员。
随后,您可以让 Derived_from_PrivatePriv_Derv 公开继承。虽然合法,但由于前面的私有继承,就好像 Derived_from_Private 没有 Base 作为基类一样。
因此,您的成员函数将无法编译:
    void memfcn(Base &b) { b = *this; }

*this 是从 Derived_from_Private 类型派生而来的,但是由于私有继承关系,它不能转换为 Base 类型,因为这两个类之间没有已知的关系。


1
但我没有使用Base的成员。即使我不将*this分配给Base,只要我有一个Base的参数(引用或副本),编译器就会给我一个错误。 - Jing
@JingleiRuan 很有趣!似乎私有继承会影响未限定名称的查找。使用::Base而不是Base来显式地引用正确(在此为全局)命名空间,就可以编译了。 - Christophe
@JingleiRuan 是的,就是这样!根据 https://dev59.com/NFgR5IYBdhLWcg3wM7Hh#41595470 的说法,这似乎是编译器的一个 bug。但是这个编译器问题并不影响我最初答案的有效性。你会发现 :: 解决方法将会编译一个空的主体,但是一旦你尝试赋值,它就会失败。 - Christophe

0

继承可以提供子类型结构扩展

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

通常使用私有继承(这并不意味着这是一种好的实践)来轻松构建非常简单的组合(具有基础,但不是基础)。


实际上,只要我将一个基础类型作为参数使用,编译器就会报错。 - Jing
你是什么意思? - Jean-Baptiste Yunès
struct Derived_from_Private : public Priv_Derv { // 非法 void memfcn(Base &b) { } }; 这也是非法的!!!为什么呢? - Jing
在这种情况下,编译器会认为你正在谈论私有基类 Base,然后抱怨,如果你想谈论在全局范围定义的类型,请写成 ::Base - Jean-Baptiste Yunès

0
一个类的名称被插入到其自身作为公共成员的范围内。这就是所谓的注入类名。在派生类Derived_from_Private中查找Base的名称时,将找到其注入类名而不是正常的类名。因为Base的注入类名被视为Base的公共成员,因此被视为Priv_Derv的私有成员,在Derived_from_Private中无法访问。

引用自[class.access.spec]第5段

[ Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared. — end note ] [ Example:

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

— end example ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接