Невозможно получить доступ к членам включающего класса из полиморфных вложенных классов - PullRequest
0 голосов
/ 27 сентября 2019

Вложенный класс Foo::Utility имеет доступ к другому вложенному классу Foo::Container, даже если последний является частным.Я пытаюсь расширить этот доступ к полиморфной версии UtilityPrint из Foo::Utility, но безуспешно:

class Foo {
private:
  class Container {};

public:
  class Utility {
  public:
    virtual void action(Container &) = 0;
    // works even if Container is private
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

// polymorphic nested class
// failed attempt
class UtilityPrint : Foo::Utility {
public:
  virtual void action(Foo::Container &) {
    /* Implementation */

    // this does not work, because Foo::Container is private
  }
};

Есть ли правильный способ достичь этого или это плохая идея для начала?

Я получаю следующее сообщение об ошибке:

error: ‘class Foo::Container’ is private
   class Container {};
         ^
error: within this context
   virtual void action(Foo::Container &) {

Кроме того, вот моя причина использования этого несколько странного дизайна: я хотел container и полиморфный utility, который делает вещик контейнеру и Foo.Так как container и utility будут использоваться только в контексте Foo, я поместил два класса в Foo.


РЕДАКТИРОВАТЬ: я могу обернуть производные Utilityв производном Foo, и код компилируется:

class Foo {
protected:
  class Container {};

public:
  class Utility {
  public:
    virtual void action(Container &) = 0;
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

class FooPrint : public Foo {

public:
  class Utility : Foo::Utility {
  public:
    virtual void action(Foo::Container &) {
      /* Implementation */
    }
  };

};

Это, однако, вводит класс-оболочку FooPrint, который существует только по синтаксическим причинам и (будучи производным классом!) никогда не предназначался для создания экземпляра,Мне не нравится такой подход по этой причине, но я могу быть очень неправ в этом отношении.

Ответы [ 2 ]

1 голос
/ 27 сентября 2019

Доступ не наследуется. Это чаще упоминается при обсуждении друзей , но это также применимо и здесь.

Сначала давайте посмотрим, почему Foo::Utility::action может получить доступ к закрытому классу Foo::Container.На самом деле, это прямо там, в именах.Foo::Container могут быть доступны только членам Foo, а члены Foo могут быть распознаны, когда вы записываете их квалифицированные имена, и это имя начинается с "Foo::".

В отличие от этого, UtilityPrint не является членом Foo.(На самом деле, было бы огромным нарушением безопасности, если бы кто-то мог просто сказать «о, да, я тоже член!» и получить доступ к вашей личной информации.) Так что пока UtilityPrint имеет(защищенный) доступ к Foo::Utility, он не имеет доступа ко всему, к чему Foo::Utility имеет доступ.

Если Foo::Utility хочет расширить свой доступ к классам, производным от него, ему потребуется явноСделай так.Один из способов сделать это - создать псевдоним.

class Utility {
  protected:
    using Container = Foo::Container;  // Derived classes can access this.
  public:
    virtual void action(Container &) = 0;
    virtual ~Utility() {}  // <-- remember to properly support polymorphism 
};

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

0 голосов
/ 28 сентября 2019

Я принял это решение :

class Foo {
protected:
  class Container {};

public:
  class Utility {
  protected:
    typedef Container FooContainer;

  public:
    virtual void action(Container &) = 0;
  };

  Container container;
  Utility * utility;

  Foo(Utility * utility): container(), utility(utility) {};

  void performAction() {
    utility -> action(container);
  }
};

class UtilityPrint : Foo::Utility {
public:
  virtual void action(FooContainer &) {
    /* implementation */
  }
};

Когда имя FooContainer вводится с использованием typedef, доступность применяется только к этому имени, независимо от его фактическогоссылаясь на Foo::Container.Это позволяет всем производным Utility ссылаться на Foo::Container, называя FooContainer.

Я считаю, что это также относится к using

...