2017-06-06 43 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创建内存泄漏的可能性。