Frage

Ich arbeite an einigen C ++ Code, wo ich mehrere Manager-Objekte mit privaten Methoden wie

haben
void NotifyFooUpdated();

, die die OnFooUpdated() Methode auf die Zuhörer dieses Objekt aufrufen.

Beachten Sie, dass sie nicht ändern, den Zustand dieses Objekts zurück, so konnten sie technisch gemacht const Methoden, obwohl sie in der Regel den Zustand des Systems als Ganzes ändern. Insbesondere nennen könnte die Zuhörer Objekte in dieses Objekt zurück und ändern Sie es.

Persönlich würde Ich mag sie verlassen, wie sie sind und sie nicht const erklären.

Aber unsere statischen Code-Checker QAC Flaggen dies als Abweichung, so dass ich entweder zu erklären, um sie const, oder ich habe zu argumentieren, warum sollen sie nicht konstante bleiben und einen Zuschuss für die Abweichung bekommen.

Was sind Argumente für diese Methoden nicht erklärt const?
Oder sollte ich QAC folgen und erklären sie const?
Sollte ich eine rein lokale Sicht auf dieses Objekt beschränkt annehmen, oder das System als Ganzes betrachten?

War es hilfreich?

Lösung

Grob gesagt Sie eine Container-Klasse haben: Ein Manager voller Beobachter. In C und C ++ können Sie const Container mit nicht konstanten Werte haben. Überlegen Sie, ob Sie eine Schicht von Verpackung entfernt:

list<Observer> someManager;

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

Sie würden sehen, nichts Ungewöhnliches über eine globale NotifyFooUpdated eine const Liste nehmen, da es sich nicht um die Liste zu ändern. Das const Argument macht tatsächlich das Argument Parsen zügiger: Die Funktion akzeptiert beide const und nicht-const-Listen. Alle const Anmerkung auf der Klassenmethode Version Mitteln ist const *this.

eine andere Perspektive zu adressieren:

Wenn Sie nicht garantieren können, dass das Objekt, das Sie die Funktion aufgerufen auf bleibt das gleiche vor und nach dem Funktionsaufruf, sollten Sie in der Regel verlassen, dass als nicht-konst.

Das ist nur sinnvoll, wenn der Anrufer die einzige Referenz auf das Objekt hat. Wenn das Objekt global oder in einer Multithread-Umgebung (wie es in der ursprünglichen Frage ist), wird die Konstantheit von einem bestimmten Anruf nicht garantieren, der Zustand des Objekts über den Aufruf unverändert ist. Eine Funktion, ohne Nebenwirkungen und der immer wieder den gleichen Wert für die gleichen Eingänge rein . NotifyFooUpdate () ist eindeutig nicht rein.

Andere Tipps

Wenn die Zuhörer als eine Sammlung von Zeigern gespeichert sind, können Sie eine nicht-const-Methode auf sie nennen, auch wenn Ihr Objekt ist konst.

Wenn der Vertrag ist, dass ein Zuhörer seinen Zustand aktualisieren kann, wenn es eine Benachrichtigung erhält, dann sollte das Verfahren nicht-konst.

Sie sagen, dass die Zuhörer in das Objekt zurückrufen kann und es ändern. Aber der Hörer wird sich nicht ändern -. So die Notify Anruf const sein könnte, aber sie passieren einen nicht konstanten Zeiger auf Ihr eigenes Objekt hinein

Wenn der Hörer bereits diesen Zeiger hat (es hört nur auf eine Sache), dann können Sie beide machen die Methoden const, als Objekt geändert zu werden, ist ein Nebeneffekt. Was geschieht, ist:

A ruft B B ändert A als Ergebnis.

So A B führt indirekt zu seiner eigenen Modifikation Aufruf ist aber nicht eine direkte Änderung des Selbst.

Wenn dies der Fall ist sowohl Ihre Methoden könnten und sollten wahrscheinlich konst.

Was sind Argumente für diese Methoden nicht erklärt const?
Oder sollte ich QAC folgen und erklären sie const?
Sollte ich eine rein lokale Sicht auf dieses Objekt beschränkt annehmen, oder das System als Ganzes betrachten?

Was Sie wissen, ist, dass das Manager-Objekt dieses für do hieß nicht ändern. Die Objekte der Manager ruft Funktionen dann auf Macht zu ändern oder sie vielleicht nicht. Sie wissen nicht, dass.

Aus Ihrer Beschreibung könnte ich eine solche Konstruktion vorstellen, in dem alle beteiligten Objekte const sind (und Benachrichtigungen können, indem sie das Schreiben an die Konsole verarbeitet werden). Wenn Sie diese Funktion nicht const machen, verbieten Sie diese. Wenn Sie es const machen, können Sie beides.

Ich denke, dies ist ein Argument für diese const zu machen.

Wenn Sie nicht garantiert werden, dass das Objekt, das Sie die Funktion auf Überreste aufgerufen das gleiche vor und nach dem Funktionsaufruf, Sie in der Regel, dass als nicht-const verlassen sollten. Denken Sie darüber nach - Sie einen Zuhörer schreiben können, legen Sie es, während das Objekt nicht-const ist, und dann diese Funktion nutzen zu const Korrektheit zu verletzen, weil Sie einmal den Zugriff auf das Objekt haben, wenn es nicht-const in der Vergangenheit war. Das ist falsch.

Mein Nehmen auf es ist, dass sie bleiben sollte nicht const . Dies basiert auf meiner Wahrnehmung, dass die Manager-Objekt des Staates, ist tatsächlich die Summe der Zustände aller Objekte, die es schafft zuzüglich intrinsische Zustand heißt State(Manager) = State(Listener0) + State(Listener1) + ... + State(ListenerN) + IntrinsicState(Manager).

Während Kapselung im Quellcode diese Laufzeit Beziehung hinwegtäuschen kann. Basierend auf Ihrer Beschreibung, ich glaube, der aggregierte Zustand des Laufzeitverhaltens des Programms reflektiert.

mein Argument zu verstärken. Ich behaupte, dass der Code sollte sich bemühen, die Programme zu reflektieren Laufzeitverhalten in den Vorzug strikte Einhaltung der genauen Semantik der Erstellung

const , oder nicht const . Das ist die Frage

Argumente für const:

  • Die Methoden in Frage nicht ändert den Zustand des Objekts.
  • Ihre statische Code-Checker Flags der Mangel an const als Abweichung, vielleicht sollten Sie es hören.

Argumente gegen const:

  • Die Methoden, um den Zustand des Systems als Ganzes ändern.
  • Listener-Objekte meinen, das Objekt ändern.

persönlich mit Ich gehe würde es als const verlassen, die Tatsache, dass es könnte den Zustand des Systems als Ganzes zu ändern ist so ziemlich wie ein Null-Zeiger-Referenz. Es ist ein const Methode, ist es nicht das Objekt in Frage zu ändern, aber es wird abstürzen Ihr Programm dadurch den Zustand des gesamten Systems zu verändern.

Es gibt einige gute Argumente für gegen const , hier also hier ist mein nehmen: -

Persönlich würde ich nicht diese „OnXXXUpdated“ als Teil meiner Manager-Klassen. Ich denke, das ist, warum einige Verwirrung dort zu Best-Practice ist. Sie benachrichtigen Interessenten über etwas, und wissen nicht, ob der Zustand des Objekts während des Benachrichtigungsprozesses ändern wird. Es kann oder auch nicht. Was is mir klar ist, dass der Prozess Beteiligten der Benachrichtigung sollte ein const sein.

Also, dieses Dilemma zu lösen, das ist, was ich tun würde:

Get von Ihren Manager-Klassen der OnXXXXUpdated Funktionen befreien.

Schreiben Sie einen Notification Manager, hier ist ein Prototyp, mit den folgenden Annahmen:

"Args" ist eine beliebige Basis-Klasse für Weitergabe von Informationen, wenn Meldungen passieren

"Delegate" ist eine Art von Funktionszeiger (z 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

Ich habe einige Code aus links, wie Sie wahrscheinlich sagen, aber Sie bekommen die Idee. Ich stelle mir vor, dass diese Notification Manager ein Singleton sein würde. Jetzt gewährleistet, dass der Notification Manager wird frühzeitig erstellt, der Rest Ihrer Manager einfach registrieren, um ihre Meldungen in der Konstruktor wie folgt aus:

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


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

Nun, wenn Ihr Manager Interessenten informieren, muss es einfach Anrufe benachrichtigen:

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

Andere Klassen für dieses Zeug hören können.

Ich habe gemerkt, dass ich habe gerade das Beobachter-Muster getippt, aber meine Absicht war, zu zeigen, dass das Problem in ist, wie diese Dinge erhoben werden und ob sie in einem const-Zustand befindet oder nicht. Durch den Prozess der Mitteilung aus der mananager Klasse abstrahiert, den Empfänger der Benachrichtigung ist frei, dass die Manager-Klasse zu ändern. Nur nicht die Benachrichtigungsmanager. Ich denke, das fair ist.

Außerdem an einem einzigen Ort mit zu erhöhen Meldungen ist gut pracice imho, wie es Ihnen an einem einzigen Ort gibt, wo Sie Ihre Mitteilungen verfolgen können.

Ich denke, Sie werden nach HICPP oder etwas ähnliches.

Was wir tun, ist, wenn unser Code QACPP verletzt und wir denken, dass es fehlerhaft ist, dann beachten wir es über Doxygen (via addtogroup Befehl, so dass Sie eine Liste von ihnen leicht bekommen), geben einen Grund jusifying warum wir es zu verletzen und dann deaktivieren Sie die Warnung über den //PRQA Befehl.

Beachten Sie, dass sie ändern nicht den Staat dieses Objekt, so konnten sie technisch hergestellt const Methoden werden, obwohl typischerweise ändern sie die Zustand des Systems als Ganzes. Im Insbesondere wendet sich die Zuhörer Macht rufen Sie in dieses Objekt zurück und ändern es.

Da der Hörer einen Zustand ändern kann, dann sollte diese Methode nicht const sein. Von dem, was Sie geschrieben haben, es klingt wie Sie viele const_cast und Anruf über Zeiger verwenden.

const Korrektheit hat eine (absichtlich wünschenswert) Art und Weise des Ausbreitungs. Sie sollten const verwenden, wo immer Sie damit durchkommen können, während const_cast und c-style-Abgüsse Artefakte im Umgang mit Client-Code sein sollten -. nie in Ihrem Code, aber sehr, sehr seltene Ausnahmen

Wenn void NotifyFooUpdated(); Anrufe listeners[all].OnFooUpdated() während auf OnFooUpdated() nicht const ist, dann sollten Sie explizit diese Mutation qualifizieren. wenn Ihr Code const korrekt im ganzen ist (was ich bin in Frage), dann macht es explizit (über Methodendeklaration / Hörer Zugang), dass Sie die Hörer (Mitglieder) mutieren, dann sollte NotifyFooUpdated() als nicht-const qualifizieren. so, man muss nur die Mutation so nah an der Quelle wie möglich erklären, und es sollte überprüfen und const-Korrektheit wird propagieren richtig.

Erstellen virtueller Funktionen const ist immer eine schwierige Entscheidung. sie nicht-const zu machen ist der einfache Ausweg. Eine Listener-Funktion in vielen Fällen const soll: wenn es (für dieses Objekt) nicht den Zuhören Aspekt ändern. Wenn zu einem Ereignis zu hören austragen sich auf die Hörposition Partei führen würde (als allgemeine Fall ist), dann sollte diese Funktion nicht-konst.

Auch wenn der interne Zustand des Objekts auf einem OnFooChanged Anruf ändern kann, auf der Ebene der Schnittstelle, das nächste Mal OnFooChanged aufgerufen wird, wird ein ähnliches Ergebnis hat. Das macht es konst.

Bei der Verwendung von const in Ihren Klassen Sie helfen den Benutzern dieser Klasse weiß, wie die Klasse mit Daten interagieren. Sie machen einen Vertrag. Wenn Sie einen Verweis auf ein konstantes Objekt haben, wissen Sie, dass alle auf diesem Objekt gemacht Anrufe werden seinen Zustand nicht ändern. Die Konstantheit dieser Referenz ist nur noch ein Vertrag mit dem Anrufer. Das Objekt ist noch frei, einige nicht-const Aktionen im Hintergrund mit änderbaren Variablen zu tun. Dies ist besonders nützlich, wenn Informationen Caching.

Zum Beispiel können Sie Klasse mit der Methode haben:

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

Dieses Verfahren eine lange Zeit kann die erste Zeit durchzuführen, sondern wird das Ergebnis für nachfolgende Aufrufe zwischenzuspeichern. Sie müssen nur sicher, dass die Header-Datei der Variablen performedFetch und valueCache als wandelbar machen erklärt.

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

    mutable bool mPerformedFetch;
    mutable int mValueCache;
};

Auf diese Weise kann ein konstantes Objekt den Vertrag mit dem Anrufer machen, und handelt wie es const ist, während ein wenig schlauer im Hintergrund arbeiten.

würde ich vorschlagen, machen Sie Ihre Klasse die Liste der Zuhörer als wandelbar erklären, und machen alles andere als const wie möglich. Soweit das Anrufer Objekt betrifft, so ist das Objekt noch const und auf diese Weise handeln.

Const bedeutet, dass der Zustand des Objekts nicht von der Elementfunktion modifiziert wird, nicht mehr und nicht weniger. Es hat nichts mit Nebenwirkungen zu tun. Also, wenn ich Ihren Fall richtig verstehen, der Zustand des Objekts nicht, dass Mittel ändert die Funktion const deklariert werden muss, wird der Zustand der anderen Teile der Anwendung hat nichts mit diesem Objekt zu tun. Auch wenn manchmal der Objektzustand nicht konstante Unterobjekte hat, die nicht Teil des logischen Zustandes des Objekts ist (z mutexes), noch die Funktionen müssen konst gemacht werden, und diese Teile müssen wandelbar deklariert werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top