继承:'A'是'B'无法访问的基础

Laz*_*zer 77 c++ inheritance

$ cat inheritance.cpp 
#include <iostream>

using namespace std;

class A { };
class B : private A { };

int main() {
    A* ab = new B;
}
$
$ g++ inheritance.cpp
inheritance.cpp: In function 'int main()':
inheritance.cpp:9: error: 'A' is an inaccessible base of 'B'
$
Run Code Online (Sandbox Code Playgroud)

我只是不明白这个错误.

据我所知,正如本教程所证实的那样,private继承只应改变class B外部世界可见成员的方式.

我认为私有说明者所做的不仅仅是改变class B成员的可见性.

  • 我该怎么得到这个错误,这是什么意思?
  • 基本上在C++中允许这种类型的代码有什么问题?看起来完全无害.

Jer*_*fin 96

通过使继承成为私有,你基本上说,即使B继承自A(根本不是)的事实是私有的 - 外部世界无法访问/可见.

如果没有进行冗长的讨论,如果允许会发生什么,那么简单的事实就是它不被允许.如果你想使用一个指向base的指针来引用派生类型的对象,那么你几乎要坚持使用公共继承.

私有继承并不一定(甚至常)打算遵循Liskov替换原则.公有继承断言派生对象可以取代基类的一个对象,和正确的语义仍然导致.但是私有继承没有断言.私人继承所暗示的关系的通常描述是"以"来实现".

公共继承意味着派生类维护基类的所有功能,并可能增加更多功能.私有继承通常或多或少地相反:派生类使用通用基类来实现具有更受限制的接口的东西.

例如,让我们假设C++标准库中的容器是使用继承而不是模板实现的.在当前系统中,std::deque并且std::vector是容器,并且std::stack是提供更受限制的接口的容器适配器.由于它基于模板,因此您可以将std::stack其用作任何一个std::deque或的适配器std::vector.

如果我们想要提供与继承基本相同的东西,我们可能会使用私有继承,所以std::stack会是这样的:

class stack : private vector {
    // ...
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们绝对希望用户能够stack像操作一样操纵我们vector.这样做可能(并且可能会)违反堆栈的期望(例如,用户可以在中间插入/移除项目,而不是按照预期的纯粹堆栈方式).我们基本上使用它vector作为一种方便的方式来实现我们的堆栈,但是如果(例如)我们改变了stack独立的实现(不依赖于基类)或者重新实现它std::deque,我们希望这样影响任何客户端代码 - 对于客户端代码,这应该只是一个堆栈,而不是一些特殊的vector(或deque).

  • 这也适用于“受保护” (2认同)
  • @KeshavSahu:哦,当然。正如老话所说,C++ 试图防范墨菲,而不是马基雅维利。换句话说,它试图保护您免受事故的影响,但如果您编写代码来绕过它的保护,它不会尝试阻止您。 (2认同)

Ben*_*igt 11

私有继承应该只改变B类成员对外界可见的方式

确实如此.而如果

A* p = new B;
Run Code Online (Sandbox Code Playgroud)

被允许,然后任何人的继承成员B可以从外部世界访问,只需通过制作一个A*.由于它们是私人继承的,因此该访问是非法的,并且上传也是如此.


Car*_*rum 7

clang++ 给出一个稍微更容易理解的错误消息:

example.cpp:9:13: error: cannot cast 'B' to its private base class 'A'
    A* ab = new B;
            ^
example.cpp:6:11: note: declared private here
class B : private A { };
          ^~~~~~~~~
1 error generated.
Run Code Online (Sandbox Code Playgroud)

我不是C++专家,但看起来它只是不被允许.我会围绕规范进行讨论,看看我想出了什么.

编辑:这是规范中的相关参考 - 第4.10指针转换,第3段:

类型为"指向cv的 指针"的prvalue D,其中D是类类型,可以转换为类型为"指向cv的指针"的prvalue B,其中B是基类D.如果B是不可访问或模糊的基类D,则需要进行此转换的程序格式不正确.


Ern*_*ill 5

它非常简单:A私有地继承的事实意味着B扩展的事实A是一个秘密,只是B"知道"它.这就是私有继承的定义.

  • 但如果我用`protected`替换`private`,我会得到同样的错误. (4认同)
  • 确实。“受保护”是指知识仅限于“ B”和“ B”的子类(和朋友)。“ A * ab = new B;`”在假设类“ C”中是合法的,该类是“ B”的子类。 (2认同)