Использование «супер» в 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 ]

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

После миграции с Turbo Pascal на C ++ в те времена я делал это для того, чтобы получить эквивалент для Turbo Pascal «унаследованного» ключевого слова, которое работает аналогично. Однако после программирования на C ++ на несколько лет я перестал это делать. Я обнаружил, что мне это не очень нужно.

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

Еще одна причина использования typedef для суперкласса - использование сложных шаблонов в наследовании объекта.

Например:

template <typename T, size_t C, typename U>
class A
{ ... };

template <typename T>
class B : public A<T,99,T>
{ ... };

В классе B было бы идеально иметь typedef для A, иначе вы застряли бы, повторяя его везде, где вы хотели бы сослаться на членов A.

В этих случаях он также может работать с множественным наследованием, но у вас не будет typedef с именем super, он будет называться base_A_t или что-то в этом роде.

- jeffk ++

1 голос
/ 24 июля 2015

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

Мое решение включает вспомогательный класс PrimaryParent и реализовано так:

template<typename BaseClass>
class PrimaryParent : virtual public BaseClass
{
protected:
    using super = BaseClass;
public:
    template<typename ...ArgTypes>
    PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){}
}

Тогда какой класс вы хотите использовать, будет объявлен так:

class MyObject : public PrimaryParent<SomeBaseClass>
{
public:
    MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {}
}

Чтобы избежать необходимости использовать виртуальное наследование в PrimaryParent для BaseClass, конструктор, принимающий переменное число аргументов, используется для построения BaseClass.

Причина наследования public BaseClass в PrimaryParent заключается в том, что MyObject имеет полный контроль над наследованием BaseClass, несмотря на наличие класса помощника между ними.

Это означает, что каждый класс, который вы хотите иметь super, должен использовать вспомогательный класс PrimaryParent, и каждый дочерний элемент может наследовать только один класс, используя PrimaryParent (отсюда и название).

Другое ограничение для этого метода: MyObject может наследовать только один класс, который наследуется от PrimaryParent, и этот должен быть унаследован с использованием PrimaryParent. Вот что я имею в виду:

class SomeOtherBase : public PrimaryParent<Ancestor>{}

class MixinClass {}

//Good
class BaseClass : public PrimaryParent<SomeOtherBase>, public MixinClass
{}


//Not Good (now 'super' is ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public SomeOtherBase{}

//Also Not Good ('super' is again ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public PrimaryParent<SomeOtherBase>{}

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

Множественное наследование является сильным инструментом, но в большинстве случаев будет только один первичный родитель, и, если есть другие родители, они, вероятно, будут классами Mixin или классами, которые в любом случае не наследуются от PrimaryParent. Если множественное наследование все еще необходимо (хотя во многих ситуациях было бы полезно использовать композицию для определения объекта вместо наследования), чем просто явно определить super в этом классе и не наследовать от PrimaryParent.

Идея определения super в каждом классе не очень привлекательна для меня, использование PrimaryParent позволяет super, явно основанному на наследовании псевдониму, оставаться в строке определения класса вместо тела класса. куда должны идти данные.

Хотя это может быть только я.

Конечно, каждая ситуация отличается, но учтите, что я сказал, когда решал, какой вариант использовать.

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

Я не знаю, редко это или нет, но я, конечно, сделал то же самое.

Как уже указывалось, сложность создания этой части самого языка заключается в том, что класс использует множественное наследование.

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

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

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

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

0 голосов
/ 16 февраля 2016

Это метод, который я использую, который использует макросы вместо typedef. Я знаю, что это не так, как в C ++, но это может быть удобно при объединении итераторов в цепочку посредством наследования, когда только базовый класс, самый дальний по иерархии, действует на унаследованное смещение.

Например:

// some header.h

#define CLASS some_iterator
#define SUPER_CLASS some_const_iterator
#define SUPER static_cast<SUPER_CLASS&>(*this)

template<typename T>
class CLASS : SUPER_CLASS {
   typedef CLASS<T> class_type;

   class_type& operator++();
};

template<typename T>
typename CLASS<T>::class_type CLASS<T>::operator++(
   int)
{
   class_type copy = *this;

   // Macro
   ++SUPER;

   // vs

   // Typedef
   // super::operator++();

   return copy;
}

#undef CLASS
#undef SUPER_CLASS
#undef SUPER

Универсальная настройка, которую я использую, позволяет очень легко читать и копировать / вставлять между деревом наследования, которые имеют повторяющийся код, но должны быть переопределены, поскольку тип возвращаемого значения должен соответствовать текущему классу.

Можно использовать строчную букву super, чтобы воспроизвести поведение, наблюдаемое в Java, но мой стиль кодирования - использовать все прописные буквы для макросов.

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

Я использую ключевое слово __super. Но это специфично для Microsoft:

http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx

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