C++ частное наследование и статические участники/типы

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

class Base
{
public:
    enum Enum
    {
        value
    };
};

class Middle : private Base
{ 
};

class Child : public Middle
{
public:
    void Method()
    {
        Base::Enum e = Base::value; // doesn't compile BAD!     
        Base* base = this; // doesn't compile GOOD!
    }
};

Я попробовал это в обоих VS2008 (требуемая версия) и VS2010, никакая работа.

Кто-либо может думать об обходном решении? Или другой подход к остановке преобразования?

Также я - сувениры поведения, это - просто побочный эффект реализации компилятора или является этим дизайном? Если дизайном, то, почему? Я всегда думал о частном наследовании, чтобы означать, что никто не знает, что середина наследовалась Основе. Однако показанное поведение подразумевает, что частное наследование означает намного больше, чем это, на самом деле у Ребенка есть меньше доступа для Базирования, чем какое-либо пространство имен не в иерархии классов!

6
задан WearyMonkey 15 June 2010 в 17:31
поделиться

2 ответа

Вы должны иметь возможность получить доступ к Base::Enum, полностью квалифицировав его:

class Child : public Middle
{
public:
    void Method()
    {
        ::Base::Enum e = ::Base::value;
    }
};

Это поведение, определенное языком (C++03 §11.2/3):

Примечание: Член частного базового класса может быть недоступен как имя наследуемого члена, но доступен напрямую.

Далее следует расширенный пример, который фактически аналогичен вашему примеру кода.

Однако, похоже, что ни Visual C++ 2008, ни Visual C++ 2010 не реализуют это правильно, поэтому, хотя вы можете использовать тип ::Base::Enum, вы все равно не можете получить доступ к ::Base::value. (На самом деле, Visual C++, похоже, во многом ошибся, поскольку он неправильно позволяет использовать не полностью квалифицированный Base::Enum).

Чтобы "обойти" эту проблему, вы можете добавить объявления using в класс Middle:

class Middle : private Base
{ 
protected:

    using Base::Enum;
    using Base::value;
};

Это не позволит вам использовать Base::Enum или Base:: value в классе Child, но это позволит вам использовать Enum и value или Middle::Enum и Middle::value.

6
ответ дан 17 December 2019 в 00:03
поделиться

У меня только один вопрос: зачем вам вообще частное наследование?

Наследование - довольно ломаная концепция, на мой взгляд, потому что она нарушает принцип One Responsibility:

  • вы наследуете интерфейс
  • вы наследуете реализацию

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

Но здесь вы явно хотите НЕ использовать полиморфизм, поэтому я задаюсь вопросом, зачем вообще использовать наследование, ведь это его единственное интересное применение (имо).

Вместо этого в C++ вы можете использовать:

  • Композицию, для повторного использования кода
  • Свободные функции (определенные в собственном пространстве имен)
  • using, typedef и т.д.... для приведения объектов извне класса

Ваш пример кажется ограниченным, но я тоже оборачиваю свои перечисления в struct, чтобы предотвратить загрязнение пространства имен тысячей символов (и потому что struct можно использовать в качестве параметров шаблона там, где пространства имен не могут).

struct MyEnum
{
  enum type
  {
    value
  };
};

class Child
{
public:
  typedef MyEnum::type Enum;

  Child(Enum e = MyEnum::value);

private:
};

Я не вижу ничего плохого в квалификации имени, наоборот, мне кажется, что это облегчает повторное чтение, поскольку вы знаете, о каком перечислении идет речь...

Действительно, частного наследования лучше избегать (и обычно заменяется Composition). Единственный допустимый случай (имо) - это оптимизация пустой базы... и, честно говоря, она нужна нечасто (как обычно бывает с оптимизациями).

1
ответ дан 17 December 2019 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: