Я пытаюсь остановить класс от способности преобразовать ее 'этот' указатель в указатель одного из ее интерфейсов. Я делаю это при помощи частного наследования через средний прокси-класс. Проблема состоит в том, что я нахожу, что частное наследование делает всех общедоступных статических участников и типы базового класса недоступными всем классам под наследующим классом в иерархии.
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, никакая работа.
Кто-либо может думать об обходном решении? Или другой подход к остановке преобразования?
Также я - сувениры поведения, это - просто побочный эффект реализации компилятора или является этим дизайном? Если дизайном, то, почему? Я всегда думал о частном наследовании, чтобы означать, что никто не знает, что середина наследовалась Основе. Однако показанное поведение подразумевает, что частное наследование означает намного больше, чем это, на самом деле у Ребенка есть меньше доступа для Базирования, чем какое-либо пространство имен не в иерархии классов!
Вы должны иметь возможность получить доступ к 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
.
У меня только один вопрос: зачем вам вообще частное наследование?
Наследование - довольно ломаная концепция, на мой взгляд, потому что она нарушает принцип 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). Единственный допустимый случай (имо) - это оптимизация пустой базы... и, честно говоря, она нужна нечасто (как обычно бывает с оптимизациями).