【问题标题】:Inheritance: 'A' is an inaccessible base of 'B'继承:'A' 是 'B' 的不可访问的基础
【发布时间】:2012-03-28 13:11:21
【问题描述】:
$ 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'
$

我只是不明白这个错误。

据我所知,正如this tutorial 所确认的那样,private 继承只会改变class B 的成员对外部世界的可见性。

我认为私有说明符不仅仅是改变class B 成员的可见性。

  • 为什么在继承时会出现这个错误,这是什么意思?
  • 在 C++ 中允许这种类型的代码基本上有什么问题?看起来完全无害。

【问题讨论】:

    标签: c++ inheritance


    【解决方案1】:

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

    没有冗长的讨论如果允许会发生什么,简单的事实就是不允许。如果您想使用指向基址的指针来引用派生类型的对象,那么您几乎无法使用公共继承。

    私有继承不一定必须(甚至通常)遵循Liskov substitution principle。公共继承断言派生对象可以替换基类的对象,并且仍然会产生正确的语义。但是,私有继承断言。对私有继承所隐含的关系的通常描述是“按照以下方式实现”。

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

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

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

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

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

    【讨论】:

    • 这也适用于protected
    【解决方案2】:

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

    确实如此。如果

    A* p = new B;
    

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

    【讨论】:

      【解决方案3】:

      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 的指针”类型的纯右值,其中 D 是类类型,可以转换为“指向 cv B 的指针”类型的纯右值,其中B 是D 的基类。如果BD 的一个不可访问或模棱两可的基类,则需要这种转换的程序是不正确的。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 但是如果我将private 替换为protected,我会得到同样的错误。
        • 确实如此。 “受保护”意味着知识仅限于BB 的子类(和朋友)。 “A* ab = new B;”在作为B 子类的假设类C 中是合法的。
        【解决方案5】:

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

        【讨论】:

        • 谢谢,但不知何故没有意义。 privates 唯一的业务应该是控制成员的行为方式。继承信息不隐藏会有什么危害?
        • 私有继承是聚合/组合的一种形式。这是一种拥有基类属性的方法,而不是成为基类的对象。如果这不是您想要的,那么私有继承不适合您。这就是它的工作原理。
        【解决方案6】:

        这是有效的

        #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;
        }
        

        【讨论】:

          猜你喜欢
          • 2017-01-12
          • 1970-01-01
          • 2011-06-18
          • 2020-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多