2012-12-09 49 views
2

我有一個不尋常的情況,C++防止複製的成員數據

說我有類似下面的類,

template <typename T> 
class C 
{ 
    public : 

    C (int size) : value_(size), some_other_member_(size) {} 

    T &value() {return value_;} 
    const T &value() const {return value_;} 

    private : 

    T value_; 
    SomeOtherType some_other_member_; 
}; 

設計,使得客戶端可以完全訪問成員value_類,就像std::vectoroperator[]會返回一個引用,這個類也是必須通過返回一個引用給客戶端完全訪問。一個二傳手/吸氣器對不會。

但是,與std::vector不同,我不想讓客戶端能夠完全替換成員。也就是說,客戶端應能夠調用const或非的value_const成員,但以下不得,

C<SomeType> c(10); 
SomeType another_value(5); 
c.value() = another_value; // This shall not be allowed 

是否有任何可能的方式,我可以給客戶全面進入value_。在某種意義上,類C應該像一個容器,但是一旦它包含的東西被初始化(通過構造函數,需要到T,但在這裏不相關),客戶端只能通過T的成員修改value_的成員職能,而不是用作業取代value_

但是,要求T是不可複製的不是我的選擇。因爲可以複製類C。問題的核心是,如類C,C有幾個成員,它們都具有size屬性,當它們被構建時,它們都被構造爲具有相同的size,如果value_被允許通過賦值被替換,那麼它允許數據結構被腐蝕,因爲成員可能不再具有相同的size屬性。

要求T只允許在大小相同時進行復制或分配也不是一種選擇。因爲,當複製一個C對象時,源和目標之間的大小可能不同。例如,

C c1(10); 
C c2(20); 
c1 = c2; 

是完全合理的。 c1的大小已更改,但其所有成員也都更改爲相同的新大小,因此它是可以的。

我希望我已經說清楚了這個問題。我總結,我想要C不會對T構成太大的限制,T基本上可以是任何帶有必需構造函數的類型。可以複製並分配T。我不希望客戶做的唯一的細點是value_C::value()

+0

如果您爲該值提供非常量引用,則可以對其進行修改。所以不要提供。一個const引用就足以讀取該值。 –

回答

3

如果您希望用戶能夠在對象上調用非常量成員函數,並且想要返回對實際對象的引用,則不能完全禁止賦值,因爲賦值運算符基本上就是這樣(你可以將a = b改寫爲a.operator=(b))。因此,您需要只返回一個const引用到您的對象,使所包含的對象non_copyable或符合事實,它可以分配給。

就我個人而言,我會建議重新考慮設計。即使你可以不允許分配,但實際上並不能保證該對象沒有成員函數,它基本上是一樣的想法(.swap(...)是一個典型的候選者),所以只要你允許,你還沒有真正贏得任何東西調用非常量成員函數。

但是,如果您只關心禁止商業機密任務,那麼您可以更難做出這樣的任務。如果您T不是內置,你可以創建一個派生類,它不公開公共賦值運算符,並返回一個參考:

template <typename T> 
class C{ 
public : 
    class Derived: public T { 
    private: 
     Derived(int size):T(size) {} 
     Derived& operator=(const Derived&) = default; //used C++11 syntax for brevity, for C++03 code it has to be implemented here 
     Derived(const Derived&) = default; //don't want this class to be copyied outside of C 
     ~Derived() = default; 
     friend class C;  
    }; 

    C (int size) : value_(size), some_other_member_(size) {} 
    Derived& value() {return value_;} 
    const Derived& value() const {return value_;} 
private : 
    Derived value_; 
    SomeOtherType some_other_member_; 
}; 

這將傳承給所有公共成員訪問,但隱藏賦值運算符(和構造函數)。當然,如果你使用C++ 11,可以通過使用/定義移動構造函數/賦值和使用完美轉發來增強此代碼,以允許使用不同的構造函數。請注意,派生的T部分仍然可以分配給使用static_cast<T&>(C.value()) = foo;

要支持不能派生自的類型(builtins ...),您需要創建一個代理,該代理將公開除分配以外的所有功能。

+0

感謝您的答案。這非常棒。雖然還有一些細節我可能需要考慮,但這是一個很好的策略,我可以使用(內部類的想法非常簡潔)。重點是防止意外分配。如果用戶想要使用某些成員函數顯式更改內部if值,則可以自行完成。我有些道理,這是他們的目標,只要他們知道他們在做什麼,他們就可以做任何他們想做的事情 –

1

至於你的getter/setter的問題,我會寫

const T& value() const; // as getter 
void value(const T&); // as setter 

返回const T&(常量引用)是完全反對像c.value() = 10情況(見例如,C++有效斯科特邁爾斯,項目23)。

我認爲這也解決了複製問題:你的課程仍然是可複製的。

+0

我在問題中提到這不是一個選項。因爲用戶將無法在不進行復制的情況下調用非常量值的成員,然後將其複製回來。這根本不是有效的,有時是不可能的。它更像是一個容器,它返回非const引用 –