2014-10-03 54 views
2

我有全局數據。從我的程序中檢索一個非常量元素const C++ - vector

每個程序模塊都必須具有讀取和寫入數據的權限。截至目前,我不使用線程,但Qt的信號和插槽,因此 - 雖然我還沒有遇到崩潰 - 我想我會需要一些時間同步。

因此,每個模塊保持這樣的數據:

const std::vector<T>& data; 

其中T是自定義類。因此,每個模塊都可以讀取數據。爲了保持一致,vector本身是const以禁止同時刪除或刪除。這些都是使用全局函數完成的(如addToData(T elem)removeFromData(int id)),它們可以同步。請注意,矢量本身被聲明爲由引用共享,因此上述全局函數中的更改將導致每個程序模塊中的數據一致。

==>這意味着可以以安全的方式讀取和添加/刪除數據。

我遇到的問題是修改數據。 T的制定者知道種族條件。使用data,我想允許調用像data.at(3).setAttr("hello"),但對於常量向量,在()處只返回常量引用。爲什麼以及如何使其工作?我可以把這個常理化掉,但那感覺不對。

考慮到我的架構,我也樂於提供建議。

+1

我會建議,而不是創建自己的類,它包裝了一個'的std :: VECTOR',提供您所需要的同步訪問。 (並且沒有全局數據) – BoBTFish 2014-10-03 10:59:46

+0

如果修改只與其他修改同步,但不與讀取同步,則會發生數據競爭,即UB。你需要一個讀寫器鎖,或者其他一些更安全的習慣用法。 – Deduplicator 2014-10-03 11:02:59

+1

我想你問的是如何解決'const'元素向量和'const'元素向量之間的模糊線條;不幸的是,C++並沒有這麼簡單。 – 2014-10-03 11:03:31

回答

2

這種情況正是您想要將常量轉移到遠處的地方。你已經仔細設計你的系統正常工作,所以不要感到嚴重關於拋棄const當你完全準備好了,它是權利要做的事情。

+2

它仍然是一個混亂,它仍然是一個黑客,它只是可以接受的,因爲這是C++中的一個缺陷。我仍然會在代碼解釋部分解釋爲什麼你覺得'const_cast'是可以接受的,以及你如何知道它是安全的。 – 2014-10-03 11:04:06

+0

@LightnessRacesinOrbit雖然我同意,但其他答案將需要對我的架構進行徹底檢查。因此,我現在將與這個答案一起,並推遲檢修:) – 2014-10-03 13:53:41

2
  1. 同步寫入離開讀取非同步可能會損壞存儲器。
  2. 鑄造恆久的氣味。

工作低開銷解決方案(雖然沒有很好地封裝)在下面。想法很好地總結了一個@ JonathanWakely的評論:「添加全局函數,它接受一個仿函數,並將其應用於非const載體」

header.h

struct no_synchronization { 
    struct mutex {} 
    struct guard { 
     guard(mutex&) {} 
    }; 
}; 

struct serialized { 
    typedef std::mutex mutex; 
    typedef std::lock_guard<mutex> guard; 
}; 

template <class T, class S = no_synchronization> 
class spaghetti { 
    typedef typename S::mutex mutex; 
    typedef typename S::guard guard; 

    static mutex mutex_; 
    static std::vector<T> vector_; 

    template <class F> 
    static void read_global_vec(F const& f) { 
     guard lock(mutex_); 
     std::vector<T> const& ref = vector_; 
     f(ref); 
    } 

    template <class F> 
    static void write_global_vec(F const& f) { 
     guard lock(mutex_); 
     f(vector_); 
    } 
} 

如果向量的內容是那麼您可以使用copy-on-write和shared_ptr來將爭用保持在最低限度。

+0

N.B. 「我不使用線程,」 – 2014-10-03 11:09:09

+1

@Jonathan「截至目前」 – Peter 2014-10-03 11:13:31

+0

@JonathanWakely - 我更新了一般情況下的答案。請注意,使同步可插拔是多麼容易。 – bobah 2014-10-03 11:19:22

2

既然你問了建議

  1. 你可以用vector,與作品大都相同的類。這也將擺脫你提到的全球功能,這是很好的。如果您希望班級的實例爲const,請在內部使用mutable向量。
  2. const_cast,但嘗試將其隱藏在某個函數的某處。
  3. 存儲智能指針。我希望我沒有錯(這是一段時間),但我認爲你可以通過const智能指針檢索非const元素。

爲了詳細說明(1),因爲我被要求在註釋:

你需要載體的功能,這對您的需求相當不錯的比賽。但是矢量的界面對於你所需要的很笨拙。這是經常遇到的情況,而包裝是默認解決方案。包裝是一個新類,它的接口符合你的需求,但它的實現幾乎只是將所有工作委託給另一個類。使用包裝的目標是使包裝對象更易於正確使用,並且更難使用錯誤。它看起來有點像這樣(沒有測試過,不會編譯):

class AWrapperForDemonstration 
{ 
public: 
    MyCustomClass& GetByIndex(int i) const // can throw 
    { 
     std::lock_guard<std::mutex> lock(_mutex); 
     return _storage[i]; 
    } 
    size_t Size(int i) const 
    { 
     std::lock_guard<std::mutex> lock(_mutex); 
     return _storage.size(); 
    } 
    void Add(MyCustomClass& addThis) 
    { 
     std::lock_guard<std::mutex> lock(_mutex); 
     _storage.push_back(addThis); 
    } 
    bool Remove(MyCustomClass& removeThis) 
    { 
     std::lock_guard<std::mutex> lock(_mutex); 
     auto it =_storage.find(removeThis); 
     if (it == _storage.end()) 
      return false; 
     _storage.erase(it); 
    } 
    template <F> void ForEach(F const& f) const 
    { 
     std::lock_guard<std::mutex> lock(_mutex); 
     for (auto& i : _storage) 
      f(i); 
    } 
private: 
    std::vector<MyCustomClass> _storage; 
    std::mutex _mutex; 
} 
+0

您可以在第一點上稍微介紹一下嗎?你會創建一個類似於bobah的類嗎? – 2014-10-03 13:28:37

+0

@Max不,完全沒有。你有一個如何使用你的矢量的概念/想法。你使用的類應該實現這個概念,並且該類在內部使用'vector'來實現。使用你的類的代碼不需要知道里面有一個'vector'。如果您稍後決定向量不是一個好選擇,您可以簡單地將它換成更適合您需要的東西,例如一個'deque'。 – Peter 2014-10-03 14:33:07

相關問題