2010-10-19 32 views
13

我工作的一些C++代碼,我有私人的方式,如我應該聲明這些方法爲const嗎?

void NotifyFooUpdated(); 

它呼籲該對象的聽衆OnFooUpdated()方法幾個經理的對象。

請注意,它們不會修改此對象的狀態,因此它們在技術上可以作爲const方法,即使它們通常會整體修改系統的狀態。特別是,監聽器對象可能會回調這個對象並對其進行修改。

個人而言,我想離開他們,因爲他們不聲明它們const。但是,我們的靜態代碼檢查器QAC會將其標記爲偏差,所以我必須聲明他們const,或者我必須爭論他們爲什麼應該保持非常量並獲得偏差授予。

什麼是不聲明這些方法const論點?
或者我應該關注QAC並聲明他們const
我是否應該嚴格限制這個對象的本地觀點,還是將整個系統看作是一個整體?

回答

3

鬆散地說,你有一個容器類:一個充滿觀察者的經理。在C和C++中,你可以使用非const值的const容器。試想,如果你刪除包裹的一層:

list<Observer> someManager; 

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

你會看到什麼奇怪的全局NotifyFooUpdated採取一個const名單,因爲它不修改該列表。該const參數實際上使參數解析更寬鬆:該函數接受const和非const列表。類方法版本上的所有const註釋的意思是const *this

爲了解決另一個角度來看:

如果你不能保證之前和函數調用後保持不變對象你調用的函數,你通常應該留下,作爲非const。

如果調用者只有對象的唯一引用,這纔是合理的。如果對象是全局對象(因爲它在原始問題中),或者在線程環境中,則任何給定調用的常量都不能保證對象的狀態在整個調用中保持不變。沒有副作用並始終爲相同輸入返回相同值的函數是。 NotifyFooUpdate()顯然不純。

+0

我想你是贊成聲明方法const? – starblue 2010-10-27 19:33:53

+2

如果這是代碼審查,我可能不會反對任何方式,除非有其他因素在起作用。如果每個人都訪問全局,並且沒有傳遞const引用,那真的沒關係。我堅決不同意靜態分析可以告訴你這個方法應該是const的前提:靜態分析告訴你它*可以*是const *被實現的*,但它不知道你是否打算稍後增強函數(例如與統計,或消息排隊或重複刪除或你有什麼)。 – 2010-10-27 21:02:06

0

我想你是跟隨HICPP或類似的東西。

我們所做的是如果我們的代碼違反了QACPP,並且我們認爲它出現錯誤,那麼我們通過Doxygen註釋它(通過addtogroup命令,因此您可以輕鬆獲得它們的列表),給出理由說明我們違反它的原因,然後通過//PRQA命令禁用該警告。

+0

是的,從技術上講,它在這裏是類似的,問題是該怎麼做,以及什麼原因。另外,我不是那種支持偏差的人,所以我需要對與我的同事爭論的問題有足夠的瞭解。 – starblue 2010-10-19 12:57:26

2

什麼是不聲明這些方法的參數const
或者我應該關注QAC並聲明他們const
我是否應該嚴格限制這個對象的本地觀點,還是將整個系統看作是一個整體?

你知道的是,這位經理反對這被稱爲用於做變化。管理員然後調用上的函數的對象可能會更改,或者他們可能不會。你不知道。

從你的描述我可以設想這樣的設計,所有涉及的對象是const(和通知可能將其寫入控制檯進行處理)。如果您不具備此功能const,則禁止此操作。如果您製作了const,則允許兩者。

我想這是有利於使這個const的參數。

+0

我認爲這會比較奇怪,因爲所有的經理都是非常量的。還要注意這些方法是私有的(我編輯了這個問題),通常調用它們的方法會修改對象的狀態。 – starblue 2010-10-19 13:08:34

3

如果偵聽器存儲爲指針集合,則即使對象爲常量,也可以在其上調用非const方法。

如果合同是,當它得到一個通知偵聽器可以更新其狀態,則該方法應該是非常量。

您是說監聽器可能會回調對象並對其進行修改。但是監聽器不會自己改變 - 所以Notify調用可以是const,但是你傳遞一個非const指針給你自己的對象。

如果監聽器已經有了這個指針(它只監聽一件事),那麼你可以使兩個方法都是const,因爲你的對象被修改是一種副作用。發生的情況是:

A調用B B因此修改A.

因此,一個調用B會間接導致它自己的修改,但不是對自我的直接修改。

如果是這種情況,您的方法可能也可能應該是const。

+0

是的,從技術上講,可以使方法爲常量,但它也是很好的「設計」?你想說盡可能地做一些事情總是很好嗎?順便說一句,監聽器被存儲爲非常量指針,並且通常會自行更改。 – starblue 2010-10-19 13:18:47

1

如果你不能保證你調用函數的對象在函數調用前後保持不變,你通常應該保持非const。考慮一下 - 你可以編寫一個監聽器,在對象是非const的時候插入它,然後使用這個函數來違反const的正確性,因爲過去它曾經是非const類型的對象時曾經訪問過這個對象。這是錯誤的。

0

注意,它們不會修改狀態時,該對象的 ,所以他們可以 技術上進行const的方法, 儘管他們通常修改系統的 國家作爲一個整體。特別是,在 特定的情況下,監聽器對象可能會調用 這個對象,並修改它 它。

由於偵聽器可以改變狀態,所以這個方法不應該是const。從你寫的內容來看,這聽起來像你正在使用大量的const_cast並通過指針調用。

+0

不,沒有涉及'const_cast',其他對象具有用於與此對象交互的非const指針。 – starblue 2010-10-26 06:35:17

+0

只要你的方法改變了某些東西,就不應該聲明爲const。就那麼簡單。 – 2010-10-26 20:09:02

0

const正確性有一個(故意要求的)傳播方式。你應該儘量使用const,而const_cast和c-style-casts應該是處理客戶端代碼的工件 - 絕不在你的代碼中,但是非常罕見的例外。

如果void NotifyFooUpdated();調用listeners[all].OnFooUpdated()OnFooUpdated()不是常量,那麼您應該明確限定此突變。如果你的代碼在整個過程中都是const正確的(我在質疑),然後使它明確(通過方法聲明/偵聽器訪問)你正在改變監聽器(成員),那麼NotifyFooUpdated()應該被視爲非const。所以,你只要將變異聲明爲儘可能靠近源,並且它應該檢出並且const正確性將正確地傳播。

0

使虛擬函數爲const通常是一個困難的決定。讓它們變成非const是簡單的方法。在很多情況下,偵聽器函數應該是const:如果它不改變偵聽方面(對於這個對象)。 如果收聽某個事件會導致聆聽方取消自己的註冊(如一般情況),那麼該函數應該是非const的。

雖然對象的內部狀態可能會在OnFooChanged調用上發生更改,但在接口級別,下一次調用OnFooChanged時會有類似的結果。這使它成爲常量。

0

在你的類中使用const時,你正在幫助該類的用戶知道該類將如何與數據交互。你正在簽訂合同。當你有一個const對象的引用時,你知道任何對該對象的調用都不會改變它的狀態。該引用的常量仍然只是與調用者的合同。該對象仍然可以使用可變變量在後臺自由地執行一些非const操作。這在緩存信息時特別有用。

例如,你可以有類方法:

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

這種方法可能需要很長的時間來執行的第一次,但將緩存後續調用的結果。您只需確保頭文件將變量performFetch和valueCache聲明爲可變。

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

    mutable bool mPerformedFetch; 
    mutable int mValueCache; 
}; 

這讓一個const對象與調用者建立契約,並且在後臺工作時稍微聰明些。

我會建議讓你的類聲明監聽器的列表爲可變的,並儘可能使其他所有的東西變爲const。就對象的調用者而言,該對象仍然是const並且是這樣的。

1

我認爲他們應該保持非常量。這是基於我的看法,即管理者對象的狀態實際上是它管理的所有對象的狀態與任何固有狀態(即,State(Manager) = State(Listener0) + State(Listener1) + ... + State(ListenerN) + IntrinsicState(Manager))的狀態的聚合。

雖然封裝在源代碼中可能認爲這種運行時間關係。根據您的描述,我相信這個聚合狀態反映了程序的運行時行爲。

爲了強化我的觀點:我聲稱代碼應該努力反映程序的運行時行爲,而不是嚴格遵守編譯的確切語義。

1

const,或不const:這是個問題。

參數爲const

  • 問題的方法不修改對象的狀態。
  • 您的靜態代碼檢查器標記爲缺少const作爲偏差,也許你應該聽它。

參數針對const

  • 的方法修改該系統的狀態作爲一個整體。
  • 監聽器對象我修改了對象。

就我個人而言,我會把它作爲const,它可能會修改整個系統的狀態非常像空指針引用。這是一個const方法,它不會修改有問題的對象,但會導致程序崩潰,從而修改整個系統的狀態。

1

有針對常量一些很好的理由,這裏所以這裏是我的看法: -

個人而言,我不會有這些「OnXXXUpdated」作爲我的經理類的一部分。我認爲這就是爲什麼最佳實踐存在一些困惑。您正在通知感興趣的各方有關某事的信息,並且不知道在通知過程中對象的狀態是否會發生變化。它可能,也可能不會。什麼對我來說很明顯,是通知感興趣方應該是一個常量的過程。

因此,要解決這一難題,這是我會做什麼:

從你的管理類擺脫OnXXXXUpdated功能。

寫通知管理器,這裏有一個原型,具有以下假設:

「參數數量」是任意基類出於傳遞信息通知時發生

「委派」是某種一個函數指針(例如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); 
}; 

其他類可以聽這個東西。

我意識到我剛剛輸入了Observer模式,但我的目的是要表明問題在於如何提出這些事情,以及它們是否處於常量狀態。通過從Mananager類中抽象出通知的過程,通知的接收者可以自由修改該經理類。只是不是通知管理員。我認爲這很公平。

此外,有一個單一的地方來提高通知是好的實踐,因爲它給你一個地方,你可以跟蹤你的通知。

0

const表示對象的狀態不會被成員函數修改,不會更多。它與副作用無關。所以如果我正確理解你的情況,對象的狀態不會改變,這意味着函數必須聲明爲const,應用程序的其他部分的狀態與此對象無關。即使有時對象狀態具有非常量子對象,它們不是對象邏輯狀態的一部分(例如互斥鎖),仍然必須使函數成爲常量,並且必須聲明那些部分是可變的。

相關問題