简体   繁体   English

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

[英]Inheritance: 'A' is an inaccessible base of 'B'

$ 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'
$

I just do not understand this error.我只是不明白这个错误。

As I understand, and as this tutorial confirms, private inheritance should only change how the members of class B are visible to the outside world.据我了解,正如本教程所确认的那样, private继承应该只改变class B成员对外界可见的方式。

I think the private specifier is doing more than just change visibility of class B members here.我认为私有说明符所做的不仅仅是改变class B成员的可见性。

  • What do I get this error and what does it mean?我得到这个错误是什么意思?
  • Basically what is wrong with allowing this type of code in C++?基本上在 C++ 中允许这种类型的代码有什么问题? Looks totally harmless.看起来完全无害。

By making the inheritance private, you're basically saying that even the fact that B inherits from A (at all) is private -- not accessible/visible to the outside world.通过将继承设为私有,您基本上是在说,即使 B 从 A 继承(根本)这一事实也是私有的——外部世界无法访问/可见。

Without getting into a long-winded discussion of what would happen if it was allowed, the simple fact is that it's not allowed.无需对如果允许会发生什么进行冗长的讨论,简单的事实是它是不允许的。 If you want to use a pointer to base to refer to an object of derived type, then you're pretty much stuck with using public inheritance.如果您想使用指向基类的指针来引用派生类型的对象,那么您几乎无法使用公共继承。

Private inheritance is not necessarily (or even normally) intended to follow the Liskov substitution principle .私有继承不一定是(甚至通常)打算遵循Liskov替换原则 Public inheritance asserts that a derived object can be substituted for an object of the base class, and proper semantics will still result.公有继承断言派生对象可以取代基类的一个对象,和正确的语义仍然导致。 Private inheritance does not assert that though.私有继承并没有断言这一点。 The usual description of the relationship implied by private inheritance is "is implemented in terms of".私有继承所隐含的关系的通常描述是“是根据……实现的”。

Public inheritance means a derived class maintains all the capabilities of the base class and potentially adds more besides.公共继承意味着派生类维护基类的所有功能,并可能添加更多功能。 Private inheritance often means more or less the opposite: that the derived class uses a general base class to implement something with a more restricted interface.私有继承通常意味着或多或少相反:派生类使用通用基类来实现具有更受限接口的东西。

Just for example, let's assume for the moment that the containers in the C++ standard library were implemented using inheritance rather than templates.仅举个例子,让我们暂时假设 C++ 标准库中的容器是使用继承而不是模板实现的。 In the current system, std::deque and std::vector are containers, and std::stack is a container adapter that provides a more restricted interface.在目前的系统中, std::dequestd::vector是容器, std::stack是容器适配器,提供了更受限的接口。 Since it is based on templates, you can use std::stack as an adapter for either std::deque or std::vector .由于它基于模板,因此您可以使用std::stack作为std::dequestd::vector的适配器。

If we wanted to provide essentially the same with inheritance, we would probably use private inheritance, so std::stack would be something like:如果我们想提供本质上相同的继承,我们可能会使用私有继承,所以std::stack将类似于:

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

In this case, we definitely do not want the user to be able to manipulate our stack as if it were a vector .在这种情况下,我们绝对希望用户能够像操作vector一样操作我们的stack Doing so could (and likely would) violate the expectations of a stack (eg, the user could insert/remove items in the middle, rather than a purely stack-like fashion as intended).这样做可能(并且可能会)违反堆栈的期望(例如,用户可以在中间插入/删除项目,而不是像预期的那样纯粹的类似堆栈的方式)。 We're basically using vector as a convenient way to implement our stack, but if (for example) we changed the implementation for stack stand alone (with no dependence on a base class) or re-implement it in terms of std::deque , we do not want that to affect any client code -- to the client code, this is supposed to be just a stack, not some specialized variety of vector (or deque).我们基本上使用vector作为实现堆栈的便捷方式,但是如果(例如)我们更改了stack独立的实现(不依赖于基类)或根据std::deque重新实现它,我们希望这影响任何客户端代码——对于客户端代码,这应该只是一个堆栈,而不是一些特殊的向量(或双端队列)。

private inheritance should only change how the members of class B are visible to the outside world私有继承应该只改变 B 类成员对外界可见的方式

It does.确实如此。 And if而如果

A* p = new B;

were allowed, then the inherited members of any B could be accessed from the outside world, just by making a A* .被允许,那么任何B的继承成员都可以从外部世界访问,只需创建一个A* Since they're privately inherited, that access is illegal, and so is the upcast.由于它们是私下继承的,因此访问是非法的,上行也是如此。

clang++ gives a slightly more understandable error message: 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.

I'm not a C++ expert, but it looks like it's just simply not allowed.我不是 C++ 专家,但看起来这只是不允许的。 I'll go poke around the spec and see what I come up with.我会去看看规范,看看我想出了什么。

Edit: here's the relevant reference from the spec - Section 4.10 Pointer conversions , paragraph 3:编辑:这是规范中的相关参考 - 第4.10指针转换,第 3 段:

A prvalue of type "pointer to cv D ", where D is a class type, can be converted to a prvalue of type "pointer to cv B ", where B is a base class of D . “指向cv D指针”类型的纯右值(其中D是类类型)可以转换为“指向 cv B指针”类型的纯右值,其中 B 是D的基类。 If B is an inaccessible or ambiguous base class of D , a program that necessitates this conversion is ill-formed.如果BD的不可访问或不明确的基类,则需要这种转换的程序是格式错误的。

It's pretty simple: the fact that A is inherited privately means that the fact that B extends A is a secret, and only B "knows" it.这很简单: A被私下继承的事实意味着B扩展A的事实是一个秘密,只有B “知道”它。 That is the very definition of private inheritance.这就是私有继承的定义。

Private inheritance means that outside the derived class, the inheritance information is hidden.私有继承意味着在派生类之外,继承信息是隐藏的。 That means you can't cast the derived class to the base class: the relationship isn't known to the caller.这意味着您不能将派生类强制转换为基类:调用者不知道这种关系。

This is working这是工作

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM