Является ли защищенное наследование переходным? - PullRequest
1 голос
/ 12 ноября 2010

Допустим, у меня есть интерфейс слушателя, определенный как:

class Listener
{
   public:
      virtual void doSomething(void) = 0;
}

и абстрактный класс, который публично наследует из моего интерфейса:

class AbstractBase: public Listener
{
   public:
      virtual void doSomething(void){ ...do something }
}

Мой производный класс наследует от моего AbstractBase используя защищено наследование:

class Derived: protected AbstractBase
{
   public:
      Derived(Caller &c){ c.register(this); }
}

Мой класс Caller уведомляет слушателей, которые регистрируются на события:

class Caller
{
   public:
      void register(Listener *listenerPtr){...add listenerPtr to some container }
      void raiseEvent(){...loop over registered listeners and call listenerPtr->doSomething();}
}

Затем я использую классы следующим образом:

int main(void)
{
   Caller caller;
   Derived derived(caller); // derived registers with caller in its constructor
   caller.raiseEvent();
}

В приведенном выше коде я вижу, что raiseEvent() разрешено вызывать listener.doSomething().Однако, поскольку я зарегистрировал экземпляр Derived в качестве прослушивателя, не должен ли doSomething() быть защищен (и, следовательно, не вызываться из Caller), поскольку он наследует свою реализацию от AbstractBase через защищенное наследование?

Ответы [ 5 ]

3 голосов
/ 12 ноября 2010

Контроль доступа основан на типе static , а не на динамическом типе.Следовательно, поскольку ваш Caller использует указатель на Listener, он может использовать интерфейс, определенный классом Listener.Если он получает указатель на Derived, он все равно в основном воспринимает его как Listener, поэтому с его точки зрения doSomething() является открытым, независимо от того, что может делать производный класс.

Каккроме того, у меня были бы вторые мысли об использовании protected наследования.Я еще не видел и не слышал ни хорошего объяснения того, что это на самом деле означает, ни разумного объяснения того, когда и почему вы хотите его использовать.В D & E Бьярне говорит (§13.9):

Защищенные базовые классы были впервые описаны в ARM и представлены в Выпуске 2.1.Оглядываясь назад, я думаю, что protected - это тот случай, когда «хорошие аргументы» и мода преодолели мое лучшее суждение и мои правила принятия новых функций.

1 голос
/ 12 ноября 2010

Просмотр его как набора интерфейсов для объекта. Он защищен в производном интерфейсе, а не в интерфейсе слушателя. Поэтому любой, кто использует интерфейс слушателя, имеет доступ к любому, кто использует производный, увидит его защищенным По сути, это проверка времени компиляции на основе объявленного типа.

0 голосов
/ 12 ноября 2010

Тот факт, что вы зарегистрировали экземпляр Derived в качестве слушателя, не имеет значения: Caller::raiseEvent манипулирует Listener, в котором метод doSomething является общедоступным.

Защищаемое отношение наследования «обходит», как только Derived регистрируется как Listener на Caller в его конструкторе: он раскрывает тот факт, что он Listener, таким образом, имея doSomething публичный метод.

0 голосов
/ 12 ноября 2010
class Derived: protected AbstractBase
{
public:
    Derived(Caller &c)
    {
            // Invokes `Caller::Register(Listener*)`. Cast to `Listener*` is
            // allowed because inside `Derived` member functions, `Listener`
            // is a visible base class. 
        c.register(this);
    }
};

class Caller
{
public:
    void register(Listener *listenerPtr);
    void raiseEvent()
    {
        // Because type of pointers is `Listener*` and not `Derived*`,
        // and because `Listener::doSomething()` is a *public*, member
        // function, it is accessile in this method.
    }
};
0 голосов
/ 12 ноября 2010

Спецификаторы доступа проверяются во время компиляции, а не во время выполнения. Если у raiseEvent() есть объект, и он знает, что он, как известно, имеет тип Listener, тогда ему разрешено вызывать doSomething() для него - это часть каждого открытого интерфейса Listener.

Если метод получит указатель на Derived объект, то через защищенное наследование будет не известно, что этот объект наследуется от AbstractBase и, следовательно, поддерживает raiseEvent() - тогда вызов будет недопустимым .

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