2011-11-30 85 views
8

我在我的類中有一個const方法,它不能更改爲非const。在這種方法中,我需要調用非const方法,但編譯器不讓我這樣做。如何從const方法調用非const方法?

有沒有辦法解決它?這裏是我的代碼的簡化示例:

int SomeClass::someMethod() const { 
    QColor saveColor = color(); 
    setColor(QColor(255,255,255)); // Calling non-const method 

    // .... 

    setColor(saveColor); // restore color 

    return 1; 
} 
+3

這不是違背常量函數的基本規則嗎? – DumbCoder

+2

@DumbCoder,從外部看,它是一個const函數,因爲客戶端沒有任何可見的變化。 –

+0

非const方法意味着它正在改變對象中的一些數據。如果'someMethod()'調用非const方法,那麼它會間接地改變數據。所以真的,'someMethod()'不應該是'const'。 –

回答

5

const-正確性的挑戰之一是你不能半途而廢。它可以是全部或全部。如果你試圖做到這一點,你最終會遇到一個像你這樣的難題。你最終會得到一個不錯的const-正確的類正在被一些瘋狂的舊的,通常遺留的(或由舊的crummudgeon編寫的)代碼使用,它不是const-正確的,它只是不起作用。你仍然想知道const-正確性是否值得所有的麻煩。

​​

你不能 - 不能直接。你也不應該。但是,還有一種替代方法...

顯然,您不能通過const方法調用非const方法。否則,const應用於成員函數時將沒有意義。

A const成員函數可以更改標記爲mutable的成員變量,但是您已經指出在您的情況下這是不可能的。

你可以嘗試做一些像SomeClass* me = const_cast<SomeClass*>(this);拋擲const內斯,但A)這通常會導致UB,或2)違反const -correctness的整體思路。

你可以做的一件事情是,如果你真的想要完成的事情能夠支持這個,那就是創建一個非const代理對象,並且做一些非const的事情。即:

#include <iostream> 
#include <string> 
using namespace std; 

class Gizmo 
{ 
public: 
    Gizmo() : n_(42) {}; 
    void Foo() const; 
    void Bar() { cout << "Bar() : " << n_ << "\n"; } 
    void SetN(int n) { n_ = n; }; 
    int GetN() const { return n_; } 
private: 
    int n_; 
}; 

void Gizmo::Foo() const 
{ 
    // we want to do non-const'y things, so create a proxy... 
    Gizmo proxy(*this); 
    int save_n = proxy.GetN(); 
    proxy.SetN(save_n + 1); 
    proxy.Bar(); 
    proxy.SetN(save_n); 
} 

int main() 
{ 
    Gizmo gizmo; 
    gizmo.Foo(); 
} 
+0

它有什麼用?我很困惑。此處的代理不是代理,因爲它不代表原始對象。它只是一個沒有超凡力量的本地物體。使用代理不等同於使用原始對象。 – Nawaz

+2

@nawaz:如果它能幫助我們以標準一致的方式完成他的工作,那麼它很有用。 –

11

你可以在this指針使用const_cast

int SomeClass::someMethod() const { 
    const_cast<SomeClass*>(this)->setColor(...);// Calling non-const method 
    //whatever 
} 

但如果這樣做,對於一個對象,最初宣佈const您遇到不確定的行爲。

所以這個:

SomeClass object; 
object.someMethod(); 

是好的,但這樣的:

const SomeClass object; 
object.someMethod(); 

產生不確定的行爲。

真正的解決方案是您的const函數首先不應該是const

+1

我想可以使用工廠和私有構造函數,以確保沒有'SomeClass'的'const'實例。但除非已經存在,否則對於類接口來說,這是一個比提問者禁止更改更大的改變,所以可能沒有多大幫助。 –

5

如何從const方法調用非const方法?

您不應該。如果拋棄this的常量,則可能會遇到未定義的行爲,使用const_cast。使用const_cast將關閉編譯器,但這不是一個解決方案。如果你需要這樣做,那麼這意味着const函數首先不應該是const。使其非常量。

或者,你應該做其他的事情,這不需要你從const函數中調用非const函數。喜歡,請不要撥打setColor功能?比如,將const函數分成多個函數(如果可以的話)?或者是其他東西?

你的具體情況,如果setColor只設置了一些成員變量,說m_color,那麼你可以聲明它mutable

mutable QColor m_color; 

,然後將其設置成你的const函數,而不調用setColor功能,且無需做const_cast

+0

這不是我剛纔寫的問題的答案,我不能將其更改爲非const。 –

+0

@Laurent:查看編輯。 – Nawaz

+2

@Laurent,如果你可以讓這個成員碰到這個函數'mutable',你有可能嗎? – Nim

6

如果您需要更改const內的一些內部狀態 - 方法,你也可以聲明受影響的狀態mutable

class Foo { 
public: 
    void doStuff() const { bar = 5; } 
private: 
    mutable int bar; 
}; 

這適用於,你有這樣的東西作爲互斥類的成員的情況。獲取和釋放互斥鎖不會影響客戶端可見狀態,但在技術上禁止使用const-方法。解決辦法是標記互斥體mutable。您的案例看起來很相似,但我認爲您的課程需要對此解決方案進行一些重構才能適用。

此外,您可能想要閱讀this answer,以瞭解如何使用RAII使這種暫時的狀態更改異常安全。