Реальные сценарии для защищенных методов

14

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

Так, каковы некоторые реальные сценарии, где я хотел бы использовать protectedметоды в коде C ++?

(Обратите внимание, что я не слишком люблю наследование реализации в целом, это может многое объяснить ...)

fredoverflow
источник

Ответы:

12

Вот пример

class Base {
public:
  // other members ...

protected:
  ~Base() { }
};

Используется как неполиморфный базовый класс. Но пользователи не смогут вызывать delete baseptr;его, потому что деструктор недоступен. Поскольку у него нет виртуального деструктора, позволять людям делать это было бы неопределенным поведением. Смотрите «Виртуальность» от Herb.

Йоханнес Шауб - Литб
источник
1
Что с вами, ребята? Почему это было отвергнуто? Это совершенно разумно. Если вы этого не понимаете, пожалуйста, спросите. Если вы чувствуете, что это неправильно, пожалуйста, объясните. Мы здесь, чтобы учиться у вас.
ВОО
Почему -1? Это первое, о чем я подумал.
GManNickG
1
Конструкторы и деструкторы - это единственное использование, которое я видел. Обратите внимание, что gcc по-прежнему выдает предупреждение, что деструктор не является виртуальным на этом.
Матье М.
+1 Я также использую защищенный метод, чтобы применить некоторые рекомендации из книг: иметь общедоступный интерфейс с защищенными виртуальными функциями вместо общедоступных виртуальных функций.
Klaim
3

Одним из примеров, который я часто использую, является то, что в Базовом классе моей иерархии объектов у меня будет защищенный регистратор. Всем моим базовым классам понадобится доступ к Logger, но нет причин делать это общедоступным.

Кроме того, если вы используете шаблон Template и у вас есть метод pre или post execute для базового класса, вы можете вызвать базовую реализацию из переопределяющего метода. Если база является только частной (и все еще может быть перезаписана в C ++), вы не сможете вызывать базовую реализацию из переопределяющего метода.

bryanatkinson
источник
1
Разве в шаблоне нет необходимости вызывать методы базового класса ???
ВОО
Точка взята, но я бы не сказал, что это «все» о том, что нет необходимости вызывать методы базового класса. Во многих ситуациях у меня есть иерархии объектов, реализующие шаблон шаблона, который имеет несколько уровней, каждый из которых добавляет немного больше функциональности / проверок. В этих случаях, Защищенный метод будет необходимо.
bryanatkinson
1

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

class Base
{
private:
    bool m_bInitialized;
public:
    virtual void Initialize() = 0;

    void setInitialized() { m_bInitialized = true; };
    bool isInitialized() const { return m_bInitialized; };
}; // eo class Base

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

class Base
{
private: 
    bool m_bInitialized;

protected:
    virtual void InitializeImpl() = 0;

public:

    void Initialize()
    {
        InitializeImpl();
        m_bInitialized = true;
    }; // eo Initialize

    bool isInitialized() const { return m_bInitialized; };
}; // eo class Base

В нашем новом классе вся инициализация по-прежнему делегируется производному классу. При условии, что было сгенерировано исключение, мы поддерживаем контракт «этот класс инициализирован», который, как говорит наш метод, произойдет.

Moo-Сок
источник
0

Как и многие другие функции, protectedпозволяет в некоторой степени нарушать инкапсуляцию. Нарушение концепции чистого ОО обычно делается по нескольким причинам.

  1. достижение лучшей производительности (думаю inline),
  2. сделать код проще для понимания, и, как ни странно,
  3. улучшенная инкапсуляция ( friendпозволяет ограничить доступ учеников к нескольким друзьям)

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

Один из случаев, когда я использовал его, - сделать все конструкторы класса protected, в основном, сделать этот класс абстрактным (его экземпляр не может быть создан, за исключением случаев, когда он является субобъектом объекта производного класса).

SBI
источник
0

Возможно, это был плохой дизайн, но у меня было что-то вроде этого:

// much simplified, of course
class input_device // base class
{
public:
    virtual ~input_device() {}

    // normally would be private with public caller, etc.
    virtual void update() = 0; 

    template <typename Func>
    void register_callback(Func func)
    {
        mButtonPressSignal.connect(func);
    }

protected:
    void trigger_signal(unsigned button)
    {
        mButtonPressSignal(button);
    }

private:
    boost::signals2::signal<void(unsigned)> mButtonPressSignal;
};

Производные классы update()могут вызывать сигнал, вызывая trigger_signal(). Но поскольку это все, что они должны делать с сигналом, сам сигнал был оставлен в тайне. Функция триггера была сделана защищенной, потому что только производный класс должен иметь возможность инициировать ее, а не что-либо вообще.

GManNickG
источник
0

«Публичные методы»: класс может сделать это. «Защищенные методы»: как класс может это сделать. «Частные методы»: как класс может это делать, но «я параноик и не хочу, чтобы кто-нибудь знал, как я это делаю».

// burguers.hpp

class BurguerClass {
  private: void addSecretRecipeSauce();  

  protected: virtual void addBread();  
  protected: virtual void addSalad();  
  protected: virtual void addMeat();
  protected: virtual void addExtraIngredients();

  public: virtual void makeBurguer();  
}

class CheeseBurguerClass: public BurguerClass {
  protected: override void addBread();  
  protected: override void addSalad();  
  protected: override void addMeat();
  protected: override void addExtraIngredients();

  protected: virtual void addCheese();

  public: override void makeBurguer();
}

class RanchStyleBurguerClass: public BurguerClass {
  protected: override void addBread();  
  protected: override void addSalad();  
  protected: override void addMeat();
  protected: override void addExtraIngredients();

  public: override void makeBurguer();
}

class EastCoastVegetarianStyleBurguerClass: public BurguerClass {
  protected: override void addBread();  
  protected: override void addSalad();  
  protected: override void addMeat();
  protected: override void addExtraIngredients();

  public: override void makeBurguer();
}

Итак, новый повар (разработчик) прибывает в ваш ресторан быстрого питания. Вы учите этому, вы продаете бургеры (общедоступные методы), как готовить бургеры (защищенные методы), но держите «запатентованный» секретный рецепт соуса при себе.

umlcat
источник