2009-12-19 50 views
3

非const函數考慮下面的代碼:間接調用一個const對象

class foo; 

foo* instance = NULL; 

class foo 
{ 
public: 
    explicit foo(int j) 
    : i(j) 
    { 
     instance = this; 
    } 

    void inc() 
    { 
     ++i; 
    } 

private: 
    int i; 
}; 

是使用定義的行爲以下?

const foo f(0); 

int main() 
{ 
    instance->inc(); 
} 

我這麼問是因爲我使用的是類註冊,和我沒有直接修改f這將是很好,使它const,但再後來就f通過註冊表修改的間接。

編輯:通過定義的行爲我的意思是:對象放置在一些特殊的內存位置,只能寫入一次?只讀內存不存在問題,至少直到C++ 1x的constexpr。恆原始類型,例如,是(常)放入只讀存儲器,並做就可以了const_cast可能會導致不確定的行爲,例如:

int main() 
{ 
    const int i = 42; 
    const_cast<int&>(i) = 0; // UB 
} 
+0

當然行 instance = this; 將不會編譯,因爲您將一個const指針(this)分配給一個非const指針(實例)。 – 2009-12-19 23:07:57

+3

@drspod:const構造函數沒有這樣的東西。這在任何構造函數中都不是const的,如果它是const的,你將如何初始化成員? – dalle 2009-12-19 23:17:51

回答

3

是的,這是未定義的行爲,如每7.1.5.1/4:

除了任何類成員聲明可變(7.1.1)可以被修改,任何試圖期間修改const對象其壽命(3.8)導致未定義的行爲。

請注意,當構造函數調用完成時(3.8/1),對象的生存期開始。

+0

謝謝。標準中的這些引用是我正在尋找的。因此,如果我將'foo :: i'標記爲可變,那麼就沒問題了。 – dalle 2009-12-20 10:32:16

+0

然後定義行爲,但你最好定義'foo instance(0);'和'foo const&f = instance;'。 – avakar 2009-12-20 10:57:12

1

這可能是在不罕見的案例之一非常著名mutable關鍵字可以使用:即使對象是常量

mutable int i;

i可以改變現在。它在邏輯上被使用對象不會改變,但實際上它確實如此。


例如:

class SomeClass 
{ 
// .... 
    void DoSomething() { mMutex.lock(); ...; } 
    mutable Mutex mMutex; 
} 

DoSomething()對象不改變邏輯,但mMutex具有以便鎖定它改變。所以它是有意義的,使其mutable,否則沒有SomeClass的實例可能是const(假設你鎖定每個操作muetx)。

+2

應該爲可能被改變但不違反引用的「常量」的數據成員使用'mutable'。例如臨時變量就是這種情況。沒有跡象表明OP的情況就是這種情況。 – shoosh 2009-12-19 23:05:23

+0

沒有跡象表明情況並非如此。我清楚地解釋了(我希望)何時應該(不)使用它;如果他選擇濫用,那麼對他來說太糟糕了。我同意,雖然我不應該說「這是其中一種情況......」,我會將其改爲「這可能是一個..」 – 2009-12-19 23:07:41

+0

「mutable」不需要,因爲我已經有了指向對象的非const指針。修改問題。 – dalle 2009-12-19 23:16:21

0

爲什麼不使用const cast?

任何使對象成爲常量的原因,儘管它的狀態不是常量?

另外,還要進行以下更改:

explicit foo(int j = 0) : i(j) 

{ instance = this; } 
+0

'const_cast'不需要,因爲我已經有了一個非const指針指向對象。 – dalle 2009-12-19 23:12:52

0

這很難說這些任意名稱的意圖。如果i僅作爲使用計數器,並且它並不真正被認爲是數據的一部分,那麼將其聲明爲mutable int i;然後當i被修改時,不會違反實例的const性。另一方面,如果i在被建模的空間中是有意義的數據,那麼這將是一件非常糟糕的事情。

另外,雖然,你的例子有點混亂,你似乎在問。 foo* instance = NULL;使用NULL作爲數字零並且初始化instance(它不是const)是有效的(如果令人困惑)那麼你分開初始化f,這是const,但從不參考它。

+0

他在構造函數中分配給'instance'。 – avakar 2009-12-19 23:34:17

0

在海灣合作委員會,至少,您的構造函數應該是explicit foo(int j)與單詞int

但是,有兩個指針指向相同的值,一個是const而另一個指針不是相當好。

+0

對不起,缺少整數。對不起,任何增加的困惑。 – dalle 2009-12-19 23:26:14

1

在const對象上調用非const(通過聲明)成員函數本身並不是非法的。你可以使用任何想要解決編譯器限制的方法:或者使用明確的const_cast,或者像你的例子那樣使用構造函數。

但是,只有在您調用的成員函數沒有嘗試實際物理地修改對象(即修改常量對象的不可變成員)時纔會定義該行爲。一旦嘗試執行修改,行爲就變得不確定。在你的情況下,方法inc修改對象,這意味着在你的例子中,行爲是未定義的。

再次調用該方法是完全合法的。

2

如果您定義了該對象的一個​​常量實例,然後丟棄該常量並修改該對象的內容,則會出現未定義的行爲。從事物的聲音,你想要的是完全相反的:創建一個非常量的對象實例,然後返回一個const指針,該對象到(大部分)客戶端,而「所有者」保留一個非const指針指向對象,以便它可以修改它認爲合適的成員。

通常情況下,通過使用專用ctor定義類來管理類似情況,因此大多數客戶端無法創建該類型的對象。然後,類將聲明所有者類爲朋友,因此它可以使用私有ctor和/或靜態成員函數來創建對象的實例(或通常只有一個實例)。所有者類然後將指針(或引用)傳遞給const對象以供客戶端使用。既不需要可變成員也不需要拋出const,因爲擁有修改對象的「正確」的所有者總是擁有一個非const指針(或者再次引用)該對象。它的客戶端只接收const指針/引用,防止修改。

+1

這樣做。將非常量指針存儲在註冊表中,並具有常量和非常量訪問器函數。 – Alan 2009-12-20 06:12:56

相關問題