Вопрос

Я работаю над кодом C++, где у меня есть несколько объектов-менеджеров с частными методами, такими как

void NotifyFooUpdated();

которые называют OnFooUpdated() метод для слушателей этого объекта.

Обратите внимание, что они не изменяют состояние этого объекта, поэтому технически их можно сделать const методы, даже несмотря на то, что они обычно изменяют состояние системы в целом.В частности, объекты-прослушиватели могут вернуться к этому объекту и изменить его.

Лично я бы хотел оставить их как есть и не объявлять их. const.

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

Каковы аргументы в пользу не объявления этих методов? const?
Или мне следует следовать QAC и заявить о них? const?
Должен ли я принять строго локальную точку зрения, ограниченную этим объектом, или рассматривать систему в целом?

Это было полезно?

Решение

Свободно говоря, у вас есть контейнерный класс: менеджер, полный наблюдателей. В C и C ++ вы можете иметь Const Containers с ценностями Non-Const. Подумайте, если вы удалили один слой упаковки:

list<Observer> someManager;

void NotifyFooUpdated(const list<Observer>& manager) { ... }

Вы не увидите ничего странного о глобальной NotifyFooupdated, принимая список Const, так как он не изменяет список. Этот конституционный аргумент на самом деле делает аргумент, разбирающийся более допустимым: функция принимает как Const, так и неконденты. Вся состочная аннотация на средстве версий метода класса const *this.

Чтобы решить другую перспективу:

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

Это только разумно, если вызывающий абонент имеет единственную ссылку на объект. Если объект глобальный (как он находится в исходном вопросе) или в резьбовой среде, постоянность любого данного вызова не гарантирует, что состояние объекта без изменений. Функция без побочных эффектов и которая всегда возвращает одинаковое значение для тех же входов чистый. Отказ Notifyfooupdate () явно не чистый.

Другие советы

Если слушатели хранятся как набор указателей, вы можете вызвать метод не-COND на них, даже если ваш объект является const.

Если договор заключается в том, что слушатель может обновить его состояние, когда он получает уведомление, то метод должен быть не-Const.

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

Если у слушателя уже есть этот указатель (он слушает только одному), то вы можете сделать как методы Const, так как ваш объект изменен, является побочным эффектом. Что происходит:

Вызывы B b изменяются в результате.

Таким образом, вызов B косвенно приводит к собственной модификации, но не является прямой модификацией себя.

Если это так, как ваши методы могут и, вероятно, должны быть const.

Что являются аргументами для не объявления этих методов const?
Или я должен следовать QAC и объявить их const?
Должен ли я принять строго локальную точку зрения, ограниченную этим объектом, или рассмотрите систему в целом?

То, что вы знаете, это то, что объект менеджера это было призвано сделать нет сдача. Объекты Менеджер тогда вызывает функции на мощь изменить или они не могут. Вы этого не знаете.

Из вашего описания я мог бы представить такой дизайн, где все вовлеченные объекты const (и уведомления могут быть обработаны, написав их на консоль). Если вы не сделаете эту функцию const, Вы запрещаете это. Если вы сделаете это const, Вы позволяете обоим.

Я думаю, это аргумент в пользу сделать это const.

Если вы не можете гарантировать, что объект, который вы вызывали функцию, остается прежним до и после вызова функций, вы обычно должны оставить это как не-Const. Подумайте об этом - вы можете написать слушатель, вставьте его, пока объект не является Const, а затем используйте эту функцию, чтобы нарушить корректность Const, потому что у вас когда-то имел доступ к этому объекту, когда он был не в прошлом. Это неверно.

Мой взять на себя это то, что они должны остаться не. Отказ Это основано на моем восприятии, что государство объекта менеджера, на самом деле является совокупностью состояний всех объектов, которые он управляет плюс любое внутреннее состояние, т. Е. State(Manager) = State(Listener0) + State(Listener1) + ... + State(ListenerN) + IntrinsicState(Manager).

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

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

К const, или нет const: вот в чем вопрос.

Аргументы для const:

  • Рассматриваемые методы не изменяют состояние объекта.
  • Ваш статический код Checker флаги от недостатка Const в качестве отклонения, может быть, вы должны слушать его.

Аргументы против const:

  • Методы изменяют состояние системы в целом.
  • Объекты слушателя мои изменения объекта.

Лично я пошел с оставлял его как const, тот факт, что это может изменить состояние системы в целом, в значительной степени похоже на ссылку на нулевой указатель. Это const Метод, оно не изменяет рассматриваемый объект, но оно потерпит вашу программу тем самым модифицировать состояние всей системы.

Есть несколько хороших аргументов для против конститут, Вот так вот мой взять: -

Лично у меня нет этих "onxxxupdated" как часть моих классов менеджера. Я думаю, что именно поэтому существует некоторая путаница для лучшей практики. Вы уверены заинтересованные стороны о чем-то и не знаете, будет ли состояние объекта изменять во время процесса уведомления. Это может или не может. Что является Очевидно для меня, состоит в том, что процесс уведомления заинтересованных сторон должен быть постоянным.

Итак, чтобы решить эту дилемму, это то, что я бы сделал:

Избавьтесь от функций onxxxxxupdated из ваших классов менеджеров.

Напишите руководитель уведомления, вот прототип, со следующими предположениями:

«Args» - это произвольный базовый класс для передачи информации, когда случаются уведомления

«Делегат» - это какой-то функциональный указатель (например, FastDelegate).

class Args
{
};

class NotificationManager
{
private:
    class NotifyEntry
    {
    private:
        std::list<Delegate> m_Delegates;

    public:
        NotifyEntry(){};
        void raise(const Args& _args) const
        {
            for(std::list<Delegate>::const_iterator cit(m_Delegates.begin());
                cit != m_Delegates.end();
                ++cit)
                (*cit)(_args);
        };

        NotifyEntry& operator += (Delegate _delegate) {m_Delegates.push_back(_delegate); return(*this); };
    }; // eo class NotifyEntry

    std::map<std::string, NotifyEntry*> m_Entries;

public:
    // ctor, dtor, etc....

    // methods
    void register(const std::string& _name);     // register a notification ...
    void unRegister(const std::string& _name);   // unregister it ...

    // Notify interested parties
    void notify(const std::string& _name, const Args& _args) const
    {
        std::map<std::string, NotifyEntry*>::const_iterator cit = m_Entries.find(_name);
        if(cit != m_Entries.end())
           cit.second->raise(_args);
    }; // eo notify

    // Tell the manager we're interested in an event
    void listenFor(const std::string& _name, Delegate _delegate)
    {
        std::map<std::string, NotifyEntry*>::const_iterator cit = m_Entries.find(_name);
        if(cit != m_Entries.end())
            (*cit.second) += _delegate;
    }; // eo listenFor
}; // eo class NotifyManager

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

MyManager::MyManager()
{
    NotificationMananger.getSingleton().register("OnABCUpdated");
    NotificationMananger.getSingleton().register("OnXYZUpdated");
};


AnotherManager::AnotherManager()
{
    NotificationManager.getSingleton().register("TheFoxIsInTheHenHouse");
};

Теперь, когда ваш менеджер должен уведомить заинтересованные стороны, это просто звонки уведомляют:

MyManager::someFunction()
{
    CustomArgs args; // custom arguments derived from Args
    NotificationManager::getSingleton().notify("OnABCUpdated", args);
};

Другие классы могут слушать этот материал.

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

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

Я думаю, вы следуете за HICPP или что-то подобное.

То, что мы делаем, если наш код нарушает QACPP, и мы думаем, что это в ошибке, то мы отмечаем его через Doxygen (через AddtOgroup Command, чтобы вы легко получить список), укажите причину, что мы нарушаем его, а затем отключить предупреждение через то //PRQA команда.

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

Поскольку слушатель может изменить состояние, то этот метод не должен быть постоянным. От того, что вы написали, звучит так, как будто вы используете много POST_CACT и вызова через указатели.

Правильность Const имеет (намеренно желательно) способ размножения. Вы должны использовать Const, где бы вы ни находились с ним, в то время как Const_Cast и C-стиль должны быть артефактами для решения с кодом клиента - никогда в вашем коде, а очень очень редкие исключения.

если void NotifyFooUpdated(); вызовы listeners[all].OnFooUpdated() в то время как дальше OnFooUpdated() это не const, то вы должны явно квалифицировать эту мутацию. Если ваш код является корректным на протяжении всего (который я задаваюсь), то сделайте его явным (через доступ к методу объявления / прослушивателя), что вы мутируете слушателей (членов), то NotifyFooUpdated() следует квалифицировать как не-постоянный. Итак, вы просто объявляете мутацию как можно ближе к источнику, и он должен проверить, а корректность Const будет распространяться должным образом.

Создание виртуальных функций Const всегда является трудным решением. Заставить их не-Const - это легкий выход. Функция слушателя должна быть постоянным во многих случаях: если она не изменяет аспект прослушивания (для этого объекта). При прослушивании события приведет к прослушиванию, чтобы вечеринка не регистрировать (как общий случай), то эта функция должна быть не-постоянного.

Хотя внутреннее состояние объекта может измениться на onfooChanged Call, на уровне интерфейса, в следующий раз вызывается onfooChanged, будет иметь аналогичный результат. Что делает его постоянным.

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

Например, вы можете иметь класс с методом:

int expensiveOperation() const
{
    if (!mPerformedFetch)
    {
        mValueCache = fetchExpensiveValue();
        mPerformedFetch = true;
    }
    return mValueCache;
}

Этот метод может занять много времени, чтобы выполнить первый раз, но кэшируйте результат для последующих вызовов. Вам нужно только убедиться, что файл заголовка объявляет переменные FoodsFeth и ValueCache в качестве Metable.

class X
{
public:
    int expensiveOperation() const;
private:
    int fetchExpensiveValue() const;

    mutable bool mPerformedFetch;
    mutable int mValueCache;
};

Это позволяет объекту Const сделать контракт с вызывающим абонентом и действовать так, как будто это const, пока работаю немного умнее на заднем плане.

Я бы предложил сделать ваш класс объявить список слушателей как Metable, и сделать все остальное как можно скорее. Что касается абонента объекта, объект все еще является постоян и действует таким образом.

Const означает, что состояние объекта не изменяется функцией-членом., Не больше, не меньше.К побочным эффектам это не имеет никакого отношения.Итак, если я правильно понимаю ваш случай, состояние объекта не меняется, это означает, что функция должна быть объявлена ​​как const, состояние других частей вашего приложения не имеет никакого отношения к этому объекту.Даже если иногда состояние объекта имеет неконстантные подобъекты, которые не являются частью логического состояния объекта (например,мьютексы), функции по-прежнему необходимо сделать константными, а эти части необходимо объявить изменяемыми.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top