Наследование: «A» является недоступной базой «B» - PullRequest
77 голосов
/ 12 марта 2012
$ 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 ++? Выглядит совершенно безвредным.

Ответы [ 5 ]

94 голосов
/ 12 марта 2012

Делая наследование частным, вы в основном говорите, что даже тот факт, что B наследует от A (вообще), является частным - не доступен / не виден внешнему миру.

Без проникновения вдолгое обсуждение того, что произойдет, если это будет разрешено, простой факт заключается в том, что это не разрешено.Если вы хотите использовать указатель на базу для ссылки на объект производного типа, то вы в значительной степени застряли с использованием публичного наследования.

Частное наследование не обязательно (или дажеобычно), чтобы следовать принципу замены Лискова .Публичное наследование утверждает, что производный объект может быть заменен объектом базового класса, и правильная семантика будет все еще результатом.Тем не менее, личное наследование не подтверждает это.Обычное описание отношений, подразумеваемых частным наследованием, «реализовано в терминах».

Открытое наследование означает, что производный класс поддерживает все возможности базового класса и потенциально добавляет еще больше.Частное наследование часто означает более или менее противоположное: что производный класс использует общий базовый класс для реализации чего-либо с более ограниченным интерфейсом.

Например, давайте на минуту предположим, что контейнеры в стандарте C ++библиотека была реализована с использованием наследования, а не шаблонов.В текущей системе std::deque и std::vector являются контейнерами, а std::stack является адаптером контейнеров, который обеспечивает более ограниченный интерфейс.Так как он основан на шаблонах, вы можете использовать std::stack в качестве адаптера для std::deque или std::vector.

Если бы мы хотели предоставить по существу то же самое с наследованием, мы бы, вероятно, использовали частное наследованиепоэтому std::stack будет выглядеть примерно так:

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

В этом случае мы определенно не хотим, чтобы пользователь мог манипулировать нашим stack, как если бы это был vector.Это может (и, вероятно, может) нарушить ожидания стека (например, пользователь может вставлять / удалять элементы посередине, а не просто как в стеке, как предполагалось).В основном мы используем vector в качестве удобного способа реализации нашего стека, но если (например) мы изменили реализацию для stack в автономном режиме (без зависимости от базового класса) или повторно реализовали ее с точки зренияstd::deque, мы не хотим, чтобы это влияло на любой клиентский код - для клиентского кода это должен быть просто стек, а не какой-то специализированный набор векторов (или deque).

11 голосов
/ 12 марта 2012

личное наследование должно только изменить то, как члены класса B видны внешнему миру

Это так. А если

A* p = new B;

было разрешено, тогда унаследованные члены любого B могли быть доступны из внешнего мира, просто сделав A*. Так как они унаследованы в частном порядке, такой доступ является незаконным, как и уклон.

7 голосов
/ 12 марта 2012

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. Если B является недоступным или неоднозначным базовым классом D, программа, которая требует этого преобразования, является плохо сформированной.

5 голосов
/ 12 марта 2012

Все довольно просто: тот факт, что A наследуется в частном порядке, означает, что факт, что B расширяет A, является секретом, и только B "знает" это. Это само определение частного наследства.

3 голосов
/ 12 марта 2012

Частное наследование означает, что за пределами производного класса информация о наследовании скрыта. Это означает, что вы не можете привести производный класс к базовому классу: отношение не известно вызывающей стороне.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...