2013-04-30 67 views
7

考慮對象:const正確性與含有對象的shared_ptr

class Obj 
{ 
    public: 
     Obj() : val(new int(1)) {} 
     int& get() {return *val;} 
     const int& get() const {return *val;} 

    private: 
     std::shared_ptr<int> val; 
}; 

正如所預期的,當對象被構造和副本由它們都可以通過的OBJ露出的shared_ptr的修改相同的值。

Obj nonconst1; 
    Obj nonconst2(nonconst1); 
    nonconst2.get() = 2; 
    cout << nonconst1.get() << ", " << nonconst2.get() << endl; 

也有可能從非const的,這似乎是做了正確的事情,它允許讀,但不能寫值的一個拷貝構造一個const Obj對象 - 如預期下面的代碼結果一個編譯錯誤:

const Obj const1(nonconst1); 
    const1.get() = 3; 

但是,可以從一個常量,其然後就允許值進行修改,以複製構造一個非const的OBJ。

Obj nonconst3(const1); 
    nonconst3.get() = 3; 

對我來說這並不覺得const正確。

有沒有辦法來防止這種行爲,同時仍然允許複製構造函數工作?在我真正的用例中,我仍然希望Obj的std容器成爲可能。

+0

也許,你應該定義複製構造函數採取非const引用,以避免複製'const'對象? – Nawaz 2013-04-30 08:34:17

+0

@Nawaz,這樣做的工作(我沒有意識到可以在複製構造函數中使用非const引用)。不幸的是,我也希望const Obj能夠複製構建其他Const obj,但嘗試「Obj(const Obj&o)const'不能編譯 – tangobravo 2013-04-30 11:56:16

回答

1

不,沒有,除非你想存儲一個shared_ptr<const int>,在這種情況下沒有人可以訪問它作爲非常量。

0

手動實現Obj的拷貝構造函數,然後應拷貝共享指針的內容。這避免了通過nonconst3修改const1的內容,因爲它們指向不同的int實例。

但是,您希望避免深度複製非常量Obj(其中沒有問題並且意在重複使用舊的共享指針)的實例。對於這一點,你必須提供const和非const拷貝構造函數只有在一個常量複製:

class Obj 
{ 
    public: 
    //... 
    Obj(Obj &o) : val(o.val) {}       // not a deep copy 
    Obj(const Obj &o) : val(std::make_shared(o.get())) {} // deep copy 
    //... 
} 
2

「對我來說這並不覺得常量,正確的」,但它是:你根本在非常量Obj上調用non const get方法。沒有錯。

如果你真的需要你後,你可以使用像一個const代理Obj但隨後的行爲,你的客戶端必須能夠處理它當然:

class Obj 
{ 
    //... 
    //original class definition goes here 
    //... 
    friend class ConstObj; 
}; 

class ConstObj 
{ 
    public: 
    ConstObj(const Obj& v) : val(v.val) {} 
    const int& get() const { return *val; } 

    private: 
    std::shared_ptr<int> val; 
}; 

//usage: 
class WorkingWithObj 
{ 
public: 
    WorkingWithObj(); 
    Obj DoSomethingYieldingNonConstObj(); 
    ConstObj DoSomethingYieldingConstObj(); 
}; 

WorkingWithObj w; 
Obj nonconst(w.DoSomethingYieldingNonConstObj()); 
nonconst.get() = 3; 

ConstObj veryconst(nonconst); 
veryconst.get() = 3; //compiler error 

ConstObj alsoconst(w.DoSomethingYieldingConstObj()); 
alsoconst.get() = 3; //compiler error 
+1

你能解釋一下你的答案嗎?我沒有看到'Obj'和使用模式中的任何內容? – Nawaz 2013-04-30 08:39:02

+0

@Nawaz添加了一些評論/使用示例。雖然我不得不說,我可能永遠不會在實際代碼中使用這樣的構造。 – stijn 2013-04-30 08:47:02

0

沒有,ISN 't ...但是,您可以使用COW,deep-copy指針,當您可以寫入value(在非const getter中)時。

或者,你可以寫兩個copy-ctorsref做淺拷貝,cref做深拷貝)。

A(A& obj) : pointer(obj.pointer) {} 
    A(const A& obj) : pointer(new int(*obj.pointer)) {} 
1

這不會破壞const正確性。由val指向的整數對象是一個不同的對象,它不是由原始對象專有的。修改其值不會影響Obj對象的狀態。

1

Is there a way to prevent this behaviour, whilst still allowing the copy constructor to work? In my real use case, I still want std containers of Obj to be possible.

你可以從一個const對象複製指定不同的拷貝構造函數 - 這意味着你可以如避免複製共享指針,而是用NULL指針創建非const對象,或者可以對指向數字執行深層副本。儘管如此,我會很謹慎地做這種事情 - 根據複製變量的常量來獲取不同的行爲是很奇怪的 - 我擔心這會讓你很難理解你的程序行爲。但是,您必須選擇一些行爲或接受當前行爲,因爲std::vector<>有時會創建副本 - 您不能簡單地將其保留爲未定義狀態。