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


82
$ 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 ++中允许这种类型的代码有什么问题?看起来完全无害。

Answers:


100

通过将继承设为私有,您基本上是在说,即使B完全继承自A的事实也是私有的-外界无法访问/看到。

没有深入讨论如果允许的话会发生什么,简单的事实是不允许这样做。如果您想使用指向基的指针来引用派生类型的对象,那么您就很难使用公共继承。

私有继承并不一定(甚至常)打算遵循Liskov替换原则。公有继承断言派生对象可以取代基类的一个对象,和正确的语义仍然导致。私有继承并没有断言。私有继承隐含的关系的通常描述是“根据……实现的”。

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

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

如果我们想提供与继承基本相同的继承,我们可能会使用私有继承,因此std::stack将类似于:

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

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


1
这也适用于protected
SubMachine

12

私有继承仅应更改外部世界对B类成员的可见方式

是的 而如果

A* p = new B;

被允许,那么任何人的继承成员B都可以通过创建来从外部访问A*。由于它们是私有继承的,因此该访问是非法的,向上传输也是如此。


8

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段:

类型的prvalue“指针CV D ”,其中D是一个类型,可以转化成类型的prvalue“指向cv B”,其中B是一个基类的D。如果B是的不可访问或不明确的基类D,则需要进行此转换的程序格式错误。


5

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


4
但是如果替换为private,我会得到同样的错误protected
Lazer 2012年

2
确实。“受保护”是指知识仅限于,B并属于的子类(和朋友)BA* ab = new B;在属于C的子类的假设类别中“ ”是合法的B
欧内斯特·弗里德曼·希尔

3

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


谢谢,但是以某种方式没有意义。private唯一的业务应该是控制成员的行为。如果不隐藏继承信息,将会有什么危害?
Lazer 2012年

1
私有继承是聚合/组合的一种形式。这是拥有基类属性而又不成为基类对象的一种方法。如果那不是您想要的,则私有继承不适合您。这就是它的工作原理。
tmpearce 2012年

0

这工作

#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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.