Использование «супер» в C ++ - PullRequest
187 голосов
/ 08 октября 2008

Мой стиль кодирования включает в себя следующую идиому:

class Derived : public Base
{
   public :
      typedef Base super; // note that it could be hidden in
                          // protected/private section, instead

      // Etc.
} ;

Это позволяет мне использовать "super" в качестве псевдонима для Base, например, в конструкторах:

Derived(int i, int j)
   : super(i), J(j)
{
}

Или даже при вызове метода из базового класса внутри его переопределенной версии:

void Derived::foo()
{
   super::foo() ;

   // ... And then, do something else
}

Он даже может быть прикован цепью (хотя я все еще должен найти применение для этого):

class DerivedDerived : public Derived
{
   public :
      typedef Derived super; // note that it could be hidden in
                             // protected/private section, instead

      // Etc.
} ;

void DerivedDerived::bar()
{
   super::bar() ; // will call Derived::bar
   super::super::bar ; // will call Base::bar

   // ... And then, do something else
}

В любом случае, я считаю использование «typedef super» очень полезным, например, когда Base либо многословен и / или шаблонизирован.

Дело в том, что super реализован в Java, а также в C # (где он называется "base", если я не ошибаюсь). Но в C ++ нет этого ключевого слова.

Итак, мои вопросы:

  • это использование typedef super common / редко / никогда не встречалось в коде, с которым вы работаете?
  • это использование typedef super Ok (т. Е. Вы видите веские или не очень веские причины не использовать его)?
  • должно ли "супер" быть хорошей вещью, должно ли оно быть несколько стандартизировано в C ++, или уже достаточно использования через typedef?

Редактировать: Родди упомянул тот факт, что typedef должен быть закрытым. Это будет означать, что любой производный класс не сможет использовать его без повторного выделения. Но я думаю, это также предотвратит цепочку super :: super (но кто за это будет плакать?).

Редактировать 2: Теперь, спустя несколько месяцев после массового использования «супер», я искренне согласен с точкой зрения Родди: «супер» должен быть закрытым. Я бы дважды подтвердил его ответ, но, думаю, не смогу.

Ответы [ 17 ]

138 голосов
/ 08 октября 2008

Бьярн Страуструп упоминает в Проектирование и развитие C ++ , что super в качестве ключевого слова рассматривался Комитетом по стандартам ISO C ++ при первом стандартизации C ++.

Даг Брук предложил это расширение, назвав базовый класс «унаследованным». В предложении упоминается проблема множественного наследования, и она помечает неоднозначные варианты использования. Даже Страуструп был убежден.

После обсуждения Даг Брук (да, тот же человек, который сделал предложение) написал, что предложение было выполнимо, технически обоснованно, не содержало серьезных недостатков и обрабатывало множественное наследование. С другой стороны, денег не хватило, и комитет должен заняться более сложной проблемой.

Майкл Тиманн опоздал, а затем показал, что superde с определением типа будет работать просто отлично, используя ту же технику, о которой спрашивалось в этом посте.

Так, нет, это, вероятно, никогда не станет стандартизированным.

Если у вас нет копии, Design and Evolution стоит стоимости обложки. Использовать копии можно примерно за 10 долларов.

93 голосов
/ 08 октября 2008

Я всегда использовал "унаследованный", а не супер. (Вероятно, из-за фона Delphi), и я всегда делаю это private , чтобы избежать проблемы, когда «унаследованное» ошибочно исключено из класса, но подкласс пытается использовать его.

class MyClass : public MyBase
{
private:  // Prevents erroneous use by other classes.
  typedef MyBase inherited;
...

Мой стандартный «шаблон кода» для создания новых классов включает в себя typedef, поэтому у меня есть небольшая возможность случайно пропустить его.

Я не думаю, что цепочка "super :: super" - хорошая идея. Если вы делаете это, вы, вероятно, очень сильно привязаны к определенной иерархии, и изменение ее, вероятно, приведет к серьезным поломкам. .

33 голосов
/ 08 октября 2008

Одна проблема с этим заключается в том, что если вы забудете (пере) определить super для производных классов, то любой вызов super :: что-то будет компилироваться нормально, но, вероятно, не вызовет нужную функцию.

Например:

class Base
{
public:  virtual void foo() { ... }
};

class Derived: public Base
{
public:
    typedef Base super;
    virtual void foo()
    {
        super::foo();   // call superclass implementation

        // do other stuff
        ...
    }
};

class DerivedAgain: public Derived
{
public:
    virtual void foo()
    {
        // Call superclass function
        super::foo();    // oops, calls Base::foo() rather than Derived::foo()

        ...
    }
};

(Как отметил Мартин Йорк в комментариях к этому ответу, эту проблему можно устранить, сделав typedef частным, а не общедоступным или защищенным.)

17 голосов
/ 08 октября 2008

FWIW Microsoft добавила расширение для __ super в свой компилятор.

14 голосов
/ 08 октября 2008

Super (или унаследованный) - очень хорошая вещь, потому что если вам нужно вставить другой слой наследования между Base и Derived, вам нужно всего лишь изменить две вещи: 1. «class Base: foo» и 2. typedef

Если я правильно помню, комитет по стандартам C ++ рассматривал вопрос о добавлении ключевого слова для этого ... пока Майкл Тиманн не указал, что этот трюк с typedef работает.

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

12 голосов
/ 11 апреля 2011

Я только что нашел альтернативное решение. У меня есть большая проблема с подходом typedef, который меня укусил сегодня:

  • Для typedef требуется точная копия имени класса. Если кто-то изменит имя класса, но не изменит typedef, у вас возникнут проблемы.

Итак, я придумал лучшее решение, используя очень простой шаблон.

template <class C>
struct MakeAlias : C
{ 
    typedef C BaseAlias;
};

Так что теперь вместо

class Derived : public Base
{
private:
    typedef Base Super;
};

у вас есть

class Derived : public MakeAlias<Base>
{
    // Can refer to Base as BaseAlias here
};

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

11 голосов
/ 08 октября 2008

Я не помню, чтобы видел это раньше, но на первый взгляд мне это нравится. Как отмечает Ферруччо , это не очень хорошо работает перед лицом MI, но MI - это скорее исключение, чем правило, и нет ничего, что говорило бы о том, что что-то нужно использовать везде, чтобы быть полезным.

8 голосов
/ 08 октября 2008

Я видел эту идиому, используемую во многих кодах, и я почти уверен, что видел ее где-то в библиотеках Boost. Однако, насколько я помню, наиболее распространенное имя - base (или Base) вместо super.

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

template <typename TText, typename TSpec>
class Finder<Index<TText, PizzaChili<TSpec> >, PizzaChiliFinder>
    : public Finder<Index<TText, PizzaChili<TSpec> >, Default>
{
    typedef Finder<Index<TText, PizzaChili<TSpec> >, Default> TBase;
    // …
}

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

3 голосов
/ 08 октября 2008

это использование typedef супер распространено / редко / никогда не встречалось в коде, с которым вы работаете?

Я никогда не видел этот конкретный шаблон в коде C ++, с которым я работаю, но это не значит, что его там нет.

это использование typedef super Ok (т. Е. Вы видите веские или не очень веские причины не использовать его)?

Он не допускает множественного наследования (в любом случае, чисто).

должно ли "супер" быть хорошей вещью, должно ли оно быть несколько стандартизировано в C ++, или уже достаточно использования через typedef?

По вышеуказанной причине (множественное наследование) нет. Причина, по которой вы видите «super» в других перечисленных вами языках, заключается в том, что они поддерживают только одиночное наследование, поэтому не возникает путаницы в отношении того, что означает «super». Конечно, в этих языках это полезно, но на самом деле ему нет места в модели данных C ++.

Да, и к вашему сведению: C ++ / CLI поддерживает эту концепцию в форме ключевого слова "__super". Обратите внимание, что C ++ / CLI также не поддерживает множественное наследование.

3 голосов
/ 08 октября 2008

Я довольно часто видел, как он используется, иногда как super_t, когда база имеет сложный тип шаблона (например, boost::iterator_adaptor делает это)

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