继承:"A"是"B"不可访问的基础

Inheritance: 'A' is an inaccessible base of 'B'

本文关键字:访问 继承      更新时间:2023-10-16
$ 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'
$

我只是不明白这个错误。

据我了解,并且正如本教程所确认的那样,private继承应该只会改变class B成员对外界的可见方式。

我认为私人指定者所做的不仅仅是改变class B成员在这里的可见性。

  • 为什么我在继承时收到此错误,这意味着什么?
  • 基本上,在C++中允许这种类型的代码有什么问题?看起来完全无害。

通过将继承设为私有,你基本上是在说,即使是 B 从 A 继承的事实(完全(也是私有的——外部世界无法访问/可见。

在不进行冗长的讨论的情况下会发生什么,简单的事实是它不允许。如果要使用指向 base 的指针来引用派生类型的对象,那么您几乎只能使用公共继承。

私人继承不一定(甚至通常(遵循利斯科夫替代原则。公共继承断言派生对象可以替换为基类的对象,并且仍然会产生正确的语义。不过,私有继承并没有断言这一点。对私人继承所隐含的关系的通常描述是"根据"实施。

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

举个例子,让我们暂时假设C++标准库中的容器是使用继承而不是模板实现的。在当前系统中,std::dequestd::vector是容器,std::stack是提供更受限制的接口的容器适配器。由于它基于模板,因此您可以将std::stack用作std::dequestd::vector的适配器。

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

class stack : private vector {
    // ...
};

在这种情况下,我们绝对不希望用户能够 操纵我们的stack就好像它是 vector .这样做可能(并且可能会(违反堆栈的期望(例如,用户可以在中间插入/删除项目,而不是预期的纯粹类似堆栈的方式(。我们基本上使用 vector 作为实现堆栈的便捷方式,但是如果(例如(我们更改了独立stack实现(不依赖于基类(或根据std::deque重新实现它,我们不希望这影响任何客户端代码——对于客户端代码,这应该只是一个堆栈, 不是一些专门的矢量(或deque(。

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

确实如此。 如果

A* p = new B;

被允许,那么任何B的继承成员都可以从外部世界访问,只需A*. 由于它们是私人继承的,因此访问是非法的,向上的人也是如此。

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.

我不是C++专家,但看起来这是不允许的。 我会去看看规格,看看我想出了什么。

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

类型为"指向 cv D 的指针"的 prvalue,其中 D 是类类型,

可以转换为类型为 "指向 cv B 的指针"的 prvalue,其中 B 是 D 的基类。如果B是一个不可访问或不明确的D基类,那么需要这种转换的程序是格式不正确的。

这很简单:A是私下继承的,这意味着B扩展A是一个秘密,只有B"知道"它。这就是私人继承的定义。

私有继承意味着在派生类之外,继承信息是隐藏的。 这意味着不能将派生类强制转换为基类:调用方不知道该关系。

这是

有效的

#include <iostream>
using namespace std;
class A{
    public:
        virtual void update() = 0;
};
class B: public A{
    public:
    virtual void update(){std::cout<<"hello";};
};
int main()
{
    A *a = new B();
    a->update();
    return 0;
}