13

私はいくつかのC++コードに取り組んでおり、次のようなプライベートメソッドを持ついくつかのマネージャーオブジェクトがあります

void NotifyFooUpdated();

OnFooUpdated()このオブジェクトのリスナーでメソッドを呼び出します。

これらはこのオブジェクトの状態を変更しないため、const通常はシステム全体の状態を変更しますが、技術的にはメソッドにすることができることに注意してください。特に、リスナーオブジェクトは、このオブジェクトをコールバックして変更する場合があります。

個人的にはそのままにしておきたいので、宣言はしませんconst

ただし、静的コードチェッカーQACはこれを逸脱としてフラグを立てるので、宣言するconstか、なぜ非定数のままにして逸脱の許可を得る必要があるのか​​を議論する必要があります。

これらのメソッドを宣言しないための引数は何constですか?
または、QACに従って、宣言する必要がありますconstか?
このオブジェクトに限定された厳密にローカルな視点を採用する必要がありますか、それともシステム全体を考慮する必要がありますか?

4

13 に答える 13

3

リスナーがポインターのコレクションとして格納されている場合は、オブジェクトがconstであっても、リスナーに対してnon-constメソッドを呼び出すことができます。

リスナーが通知を受け取ったときに状態を更新できるという契約の場合、メソッドは非定数である必要があります。

あなたは、リスナーがオブジェクトにコールバックしてそれを変更するかもしれないと言っています。ただし、リスナーはそれ自体を変更しません。したがって、Notify呼び出しはconstである可能性がありますが、const以外のポインターを独自のオブジェクトに渡します。

リスナーがすでにそのポインターを持っている場合(1つのものだけをリッスンする)、オブジェクトが変更されるのは副作用であるため、両方のメソッドをconstにすることができます。何が起こっているのですか:

AがBBを呼び出すと、結果としてAが変更されます。

したがって、Bを呼び出すAは、それ自体の変更に間接的につながりますが、自己の直接的な変更ではありません。

この場合、両方のメソッドがconstである可能性があり、おそらくconstである必要があります。

于 2010-10-19T13:13:21.397 に答える
3

大まかに言えば、コンテナクラスがあります。オブザーバーでいっぱいのマネージャーです。CおよびC++では、const以外の値を持つconstコンテナーを持つことができます。1層のラッピングを削除したかどうかを検討してください。

list<Observer> someManager;

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

グローバルNotifyFooUpdatedがconstリストを取得することについては、リストが変更されないため、何も奇妙なことはありません。そのconst引数は、実際には引数の解析をより許容的にします。この関数は、constリストとnon-constリストの両方を受け入れます。クラスメソッドバージョンのすべてのconstアノテーションはですconst *this

別の観点に対処するには:

関数を呼び出したオブジェクトが関数呼び出しの前後で同じままであることを保証できない場合は、通常、それをnon-constのままにしておく必要があります。

これは、呼び出し元がオブジェクトへの唯一の参照を持っている場合にのみ合理的です。オブジェクトがグローバル(元の質問のように)またはスレッド環境にある場合、特定の呼び出しの恒常性は、オブジェクトの状態が呼び出し全体で変更されないことを保証しません。副作用がなく、同じ入力に対して常に同じ値を返す関数は純粋です。NotifyFooUpdate()は明らかに純粋ではありません。

于 2010-10-27T18:25:09.427 に答える
2

これらのメソッドを宣言しないための引数は何constですか?
または、QACに従って、宣言する必要がありますconstか?
このオブジェクトに限定された厳密にローカルな視点を採用する必要がありますか、それともシステム全体を考慮する必要がありますか?

あなたが知っていることは、これが要求されたそのマネージャオブジェクトは変更されないということです。次にマネージャーが関数を呼び出すオブジェクトは変更される場合と変更されない場合があります。あなたはそれを知りません。

あなたの説明から、関連するすべてのオブジェクトが存在するような設計を想像することができますconst(そして、通知はコンソールに書き込むことによって処理される可能性があります)。この機能を作成しない場合は、これconstを禁止します。あなたがそれを作るならばconst、あなたは両方を許します。

私はこれがこれを作ることを支持する議論だと思いますconst

于 2010-10-19T13:01:48.707 に答える
1

関数を呼び出したオブジェクトが関数呼び出しの前後で同じままであることを保証できない場合は、通常、それをnon-constのままにしておく必要があります。考えてみてください。リスナーを作成し、オブジェクトが非constのときに挿入し、この関数を使用してconstの正当性に違反する可能性があります。これは、過去に非constのときにそのオブジェクトにアクセスしたことがあるためです。それは間違っている。

于 2010-10-23T11:06:46.770 に答える
1

私の見解は、それらは非定数のままでなければならないということです。これは、マネージャーオブジェクトの状態は、実際には、マネージャーオブジェクトが管理するすべてのオブジェクトの状態に、固有の状態を加えたものであるという私の認識に基づいていますState(Manager) = State(Listener0) + State(Listener1) + ... + State(ListenerN) + IntrinsicState(Manager)

ソースコードでのカプセル化は、この実行時の関係を信じている可能性があります。あなたの説明に基づくと、この集約された状態は、プログラムの実行時の動作を反映していると思います。

私の議論を補強するために:私は、コードがコンパイルの正確なセマンティクスに厳密に従うよりも、プログラムの実行時の動作を反映するように努めるべきであると断言します。

于 2010-10-28T18:20:00.433 に答える
1

生きるべき constか、死ぬべきか const:それが問題です。

の引数const

  • 問題のメソッドは、オブジェクトの状態を変更しません。
  • 静的コードチェッカーは、constの欠如を逸脱としてフラグを立てます。おそらく、それを聞く必要があります。

に対する議論const

  • これらのメソッドは、システム全体の状態を変更します。
  • リスナーオブジェクトはオブジェクトを変更します。

個人的には、そのままにしておくことにしconstます。システム全体の状態を変更する可能性があるという事実は、nullポインター参照に非常によく似ています。これはconstメソッドであり、問​​題のオブジェクトを変更しませんが、プログラムをクラッシュさせ、それによってシステム全体の状態を変更します。

于 2010-10-28T23:23:01.803 に答える
1

constに反対するためのいくつかの良い議論があります、ここでここに私の見解があります:-

個人的には、これらの「OnXXXUpdated」をマネージャークラスの一部として持っていません。これが、ベストプラクティスに関して混乱が生じる理由だと思います。利害関係者に何かについて通知していて、通知プロセス中にオブジェクトの状態が変更されるかどうかがわかりません。それはそうかもしれないし、そうでないかもしれない。私にとって明らかなことは、利害関係者に通知するプロセスは一定でなければならないということです。

したがって、このジレンマを解決するために、これは私が行うことです。

マネージャクラスからOnXXXXUpdated関数を削除します。

次の前提条件で、通知マネージャーを作成します。これがプロトタイプです。

「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

おそらくお分かりのように、いくつかのコードを省略しましたが、あなたはその考えを理解しています。このNotificationManagerはシングルトンになると思います。これで、Notification Managerが早い段階で作成されるようになり、残りのマネージャーは次のようにコンストラクターに通知を登録するだけです。

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


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

これで、マネージャーが利害関係者に通知する必要がある場合、単にnotifyを呼び出します。

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

他のクラスはこのようなものを聞くことができます。

オブザーバーパターンを入力したばかりだと気づきましたが、問題はこれらがどのように発生しているのか、そしてそれらがconst-stateにあるかどうかにあることを示すことでした。マネージャークラスから通知のプロセスを抽象化することにより、通知の受信者はそのマネージャークラスを自由に変更できます。通知マネージャーではありません。これは公平だと思います。

その上、通知を上げるための単一の場所を持つことは、通知を追跡できる単一の場所を提供するので、良い習慣です。

于 2010-10-29T13:01:52.003 に答える
0

あなたはHICPPかそれに類似したものをフォローしていると思います。

コードがQACPPに違反していて、エラーであると思われる場合は、Doxygenを介して(addtogroupコマンドを介して、それらのリストを簡単に取得できるように)メモし、違反している理由を判断する理由を示してから、警告を無効にします。//PRQAコマンドを介して。

于 2010-10-19T12:46:21.977 に答える
0

これらはこのオブジェクトの状態を変更しないため、通常はシステム全体の状態を変更しますが、技術的にはconstメソッドにすることができることに注意してください。特に、リスナーオブジェクトは、このオブジェクトをコールバックして変更する場合があります。

リスナーは状態を変更できるため、このメソッドはconstであってはなりません。あなたが書いたことから、あなたはたくさんのconst_castを使用していて、ポインタを介して呼び出すように思えます。

于 2010-10-25T20:58:44.597 に答える
0

constの正当性には、(意図的に望ましい)伝播方法があります。const_castとc-style-castsは、クライアントコードを処理するためのアーティファクトである必要がありますが、コードでは決して使用できませんが、非常にまれな例外です。

オン中のvoid NotifyFooUpdated();呼び出しがconstでない場合は、このミューテーションを明示的に修飾する必要があります。コードが(私が質問している)全体でconstが正しい場合は、リスナー(メンバー)を変更していることを(メソッド宣言/リスナーアクセスを介して)明示的にし、non-constとして認定する必要があります。したがって、可能な限りソースに近いミューテーションを宣言するだけで、チェックアウトする必要があり、const-correctnessが適切に伝播されます。listeners[all].OnFooUpdated()OnFooUpdated()NotifyFooUpdated()

于 2010-10-25T21:27:37.303 に答える
0

仮想関数をconstにすることは、常に難しい決断です。それらを非定数にするのは簡単な方法です。多くの場合、リスナー関数はconstである必要があります。それがリスニングアスペクトを変更しない場合(このオブジェクトの場合)。イベントをリッスンすると、リスニングパーティが(一般的なケースとして)自分自身の登録を解除する場合、この関数は非定数である必要があります。

オブジェクトの内部状態はOnFooChanged呼び出しで変更される可能性がありますが、インターフェイスのレベルでは、次にOnFooChangedが呼び出されたときに同様の結果になります。それはそれをconstにします。

于 2010-10-26T10:50:03.393 に答える
0

クラスでconstを使用すると、そのクラスのユーザーがクラスがデータとどのように相互作用するかを知ることができます。あなたは契約を結んでいます。constオブジェクトへの参照がある場合、そのオブジェクトに対して行われた呼び出しはその状態を変更しないことがわかります。その参照の恒常性は、依然として呼び出し元との契約にすぎません。オブジェクトは、可変変数を使用して、バックグラウンドで非定数アクションを自由に実行できます。これは、情報をキャッシュするときに特に役立ちます。

たとえば、次のメソッドを使用してクラスを作成できます。

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

このメソッドは、最初の実行に長い時間がかかる場合がありますが、後続の呼び出しのために結果をキャッシュします。ヘッダーファイルが変数performedFetchとvalueCacheを可変として宣言していることを確認する必要があるだけです。

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

    mutable bool mPerformedFetch;
    mutable int mValueCache;
};

これにより、constオブジェクトは呼び出し元とコントラクトを作成し、バックグラウンドで少しスマートに動作しながら、constのように動作します。

クラスにリスナーのリストを可変として宣言させ、他のすべてを可能な限りconstにすることをお勧めします。オブジェクトの呼び出し元に関する限り、オブジェクトは引き続きconstであり、そのように動作します。

于 2010-10-26T21:09:02.717 に答える
0

Constは、オブジェクトの状態がメンバー関数によって変更されないことを意味します。それ以上でもそれ以下でもありません。それは副作用とは何の関係もありません。したがって、私があなたのケースを正しく理解していれば、オブジェクトの状態は変化しません。つまり、関数はconstとして宣言する必要があり、アプリケーションの他の部分の状態はこのオブジェクトとは関係ありません。オブジェクトの状態に、オブジェクトの論理状態の一部ではない非constサブオブジェクト(ミューテックスなど)がある場合でも、関数をconstにする必要があり、それらの部分を可変として宣言する必要があります。

于 2010-10-29T20:45:36.977 に答える