2017-06-06 57 views
3

我有一個簡單的類結構建模離散模擬,其中包含一個狀態矢量,每個狀態矢量都包含多個Transitions,並保存爲智能指針的矢量。我使用智能指針來保存轉換,因爲在我的完整應用程序中我需要多態。將unique_ptr對象推入C++的向量中

#include <vector> 
#include <memory> 

class Transition { 
    public: 
     Transition() {} 
}; 


class State { 
    public: 
     State(int num) : num(num), transitions() {} 
     void add_transition(std::unique_ptr<Transition> trans) { 
      transitions.push_back(std::move(trans)); 
     } 

    private: 
     int num; 
     std::vector<std::unique_ptr<Transition>> transitions; 
}; 


int main() { 
    std::vector<State> states; 
    for (int i = 0; i < 10; i++) { 
     State nstate = State(i); 
     for (int j = 0; j < 2; j++) { 
      nstate.add_transition(std::move(std::unique_ptr<Transition>(new Transition()))); 
     } 
     // This line causes compiler errors 
     states.push_back(nstate); 
    } 
} 

我添加新的狀態對象爲載體時得到編譯器錯誤:

Error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Transition; _Dp = std::default_delete<Transition>]’ 
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); } 

我想這是由於矢量使國家物體也試圖使一個副本的副本unique_ptrs這是不允許的矢量的。我已經看到emplace_back不會像push_back那樣製作副本,但我仍然得到相同的錯誤。

將狀態對象直接添加到矢量工程中,但我更願意避免這種解決方法,因爲在我的實際代碼中,我使用State對象做了更多的工作,而不是僅僅添加轉換並且不想繼續訪問向量。

int main() { 
    std::vector<State> states; 
    for (int i = 0; i < 10; i++) { 
     states.push_back(State(i)); 
     for (int j = 0; j < 2; j++) { 
      states[i].add_transition(std::move(std::unique_ptr<Transition>(new Transition()))); 
     } 
    } 
} 
+0

添加一個移動構造函數,你可以默認它。 – Nim

回答

3

State不可複製,但只能移動;但對於states.push_back(nstate);nstate是一個左值(作爲命名變量),無法從中移出。然後複製試圖執行,但不允許。

爲了解決這個問題,你可以使用std::move(把它變成一個右值):

states.push_back(std::move(nstate)); 

LIVE


注意,移動操作後,nstate數據成員(包括矢量及其內容)也將被移動。

+0

這個工作,而不需要創建一個默認的移動構造函數 –

+0

@StuartLacy它'State'隱含真實產生的。 – songyuanyao

+0

它沒有完全解決問題。在'push_back'結尾處,'nstate'將是空的(保持'nullptr')並且讓操作只能通過取消引用他們希望不做的數組來訪問狀態。如果這是該計劃,則在'push_back'之前帶上一個'State * pnstate = nstate.get()'指針。 – Persixty

1

你需要實現一個移動構造函數爲您State和呼叫std::move移動的對象

class State { 
public: 
    // default if you just want it to move the members one by one 
    State(State&& s) = default; 
}; 

states.push_back(std::move(nstate)); 
+0

代碼運行時沒有指定移動構造函數,它的默認聲明是顯式還是其他角色? –

+0

@StuartLacy在某些情況下,移動構造函數將由編譯器隱式創建。在其他一些條件下,它不會,那麼需要一個明確違約的條件。把它寫下來無論哪種方式,就是很好的做法,並作爲文檔 –

3

你需要做的所有權決定。

new分配對象的所有者(或所有者)負責確保它在其生命週期結束時被「刪除」。

如果vector<>擁有該對象然後std::move()std::unique_ptr<>vector,並繼續通過一個「原始」指針來訪問該對象但如果vector被破壞或std::unique_ptr被擦除/重設將被無效。

如果vector<>不擁有對象而不是聲明它vector<State*>並且承認它會在std::unique_ptr遭到破壞(除非您介入)時失效。

如果存在複雜的關係,請考慮std::shared_ptr<>這將允許多個對象共享所有權,但要確保無法進行循環引用。

除此之外,您將進入更復雜的所有權模式和可能的「垃圾回收」。

表面檢查表明一個'國家'可能擁有它的Transition s,因爲總體而言,它們在國家存在時是有意義的,當它不存在時則停止有意義。因此,繼續vector<std::unique_ptr<> >並訪問State和/或Transition作爲指針。

如果這對您的情況不起作用,您可能需要一個擁有所有狀態和所有轉換的'FiniteState'上下文對象,並注意刪除所有狀態以及所有相關轉換。狀態被破壞。

1
// This line causes compiler errors 
states.push_back(nstate); 

nstate對象是State類的一個實例。所述State類包含兩個數據成員:一個int(這是可複製),以及unique_ptrvector,即移動,但不可拷貝(因爲unique_ptr是可移動的,但不可拷貝)。因此,整個State類是可移動的,但不可複製。所以,你必須std::movenstate對象到states載體:

states.push_back(std::move(nstate)); 

如果你想副本語義,你應該使用的shared_ptr S(被引用計數智能指針的載體,並可複製和可移動)。


我也做了一些修改,以你的State類代碼:

class State { 
    public: 
     State(int num) : num(num), transitions() {} 

在這裏,你應該標記構造explicit,避免int轉換。而且,std::vector數據成員會自動初始化,這裏不需要使用transitions()

此外,考慮到這行代碼:

states[i].add_transition(std::move(std::unique_ptr<Transition>(new Transition()))); 

你應該使用std::make_unique(在C++ 14引入),而不是與由顯式調用返回的原始指針構建std::unique_ptrnew

+0

煩人我堅持用C++ 11,但會記住這一點 –

2

您應該避免使用push_back並使用emplace_back替代地創建項目。

constexpr ::std::int32_t const states_count{10}; 
constexpr ::std::int32_t const transitions_per_state_count{2}; 
::std::vector<State> states; 
states.reserve(states_count); 
for(::std::int32_t state_index{}; states_count != state_index; ++state_index) 
{ 
    states.emplace_back(state_index); // new state is added without copying or moving anything 
    auto & nstate{states.back()}; 
    for(::std::int32_t transition_index{}; transitions_per_state_count != transition_index; ++transition_index) 
    { 
     nstate.add_transition(::std::unique_ptr<Transition>{new Transition{}}); 
    } 
} 
+0

由於C++ 17:'汽車及NSTATE = states.emplace_back(state_index);' – zett42

1

你傳入std::unique_ptr<Transition>按值的功能,應在void add_transition(std::unique_ptr<Transition> trans)創建一個本地副本。

如果您將通過參考std::unique_ptr<Transition>& trans傳遞價值,您將不需要任何std::moveadd_transition函數以外。你

可能還需要使用std::make_unique<Transition>()而不是std::uniqye_ptr<Transition>(new Transition())

封裝的

new關鍵字使你的代碼更清晰,decreeses創建內存泄漏的可能性。