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

我只是不明白這個錯誤。

據我了解,正如本教程所確認的那樣, private繼承應該只改變class B成員對外界可見的方式。

我認為私有說明符所做的不僅僅是改變class B成員的可見性。

  • 我得到這個錯誤是什么意思?
  • 基本上在 C++ 中允許這種類型的代碼有什么問題? 看起來完全無害。

通過將繼承設為私有,您基本上是在說,即使 B 從 A 繼承(根本)這一事實也是私有的——外部世界無法訪問/可見。

無需對如果允許會發生什么進行冗長的討論,簡單的事實是它是不允許的。 如果您想使用指向基類的指針來引用派生類型的對象,那么您幾乎無法使用公共繼承。

私有繼承不一定是(甚至通常)打算遵循Liskov替換原則 公有繼承斷言派生對象可以取代基類的一個對象,和正確的語義仍然導致。 私有繼承並沒有斷言這一點。 私有繼承所隱含的關系的通常描述是“是根據……實現的”。

公共繼承意味着派生類維護基類的所有功能,並可能添加更多功能。 私有繼承通常意味着或多或少相反:派生類使用通用基類來實現具有更受限接口的東西。

僅舉個例子,讓我們暫時假設 C++ 標准庫中的容器是使用繼承而不是模板實現的。 在目前的系統中, std::dequestd::vector是容器, std::stack是容器適配器,提供了更受限的接口。 由於它基於模板,因此您可以使用std::stack作為std::dequestd::vector的適配器。

如果我們想提供本質上相同的繼承,我們可能會使用私有繼承,所以std::stack將類似於:

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

在這種情況下,我們絕對希望用戶能夠像操作vector一樣操作我們的stack 這樣做可能(並且可能會)違反堆棧的期望(例如,用戶可以在中間插入/刪除項目,而不是像預期的那樣純粹的類似堆棧的方式)。 我們基本上使用vector作為實現堆棧的便捷方式,但是如果(例如)我們更改了stack獨立的實現(不依賴於基類)或根據std::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指針”類型的純右值(其中D是類類型)可以轉換為“指向 cv B指針”類型的純右值,其中 B 是D的基類。 如果BD的不可訪問或不明確的基類,則需要這種轉換的程序是格式錯誤的。

這很簡單: 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM