我有一个简单的类结构,它们具有一个带有状态向量的离散模拟的模拟,每个模拟都包含许多过渡,作为智能指针的向量。我已经使用智能指针来保持过渡,就像我的完整应用一样,我需要多态性。
#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
这样的副本,但我仍然遇到相同的错误。
将状态对象直接添加到向量中有效,但是我希望避免使用此解决方法,就像我在实际代码中一样,我可以与状态对象进行更多工作,而不仅仅是添加过渡,也不想继续访问向量。
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())));
}
}
}
State
是不可复制的,而是可移动的;但是对于states.push_back(nstate);
,nstate
是一个LVALUE(作为命名变量(,无法从中移动。然后尝试执行副本,但不允许进行。
要解决它,您可以使用std::move
(将其转换为rvalue(:
states.push_back(std::move(nstate));
live
请注意,移动操作后,nstate
的数据成员(包括向量及其内容(也将被移动。
您需要做出所有权决定。
new
的所有者(或所有者(分配的对象负责确保其在其生命周期结束时被删除。
如果vector<>
拥有对象,则std::move()
将std::unique_ptr<>
纳入vector
,然后继续通过" RAW"指针访问对象,但如果vector
被破坏或std::unique_ptr
被擦除/RESET/RESET。
如果vector<>
不拥有该对象,则在破坏std::unique_ptr
时会识别其无效(除非您进行干预(。
如果存在复杂的关系,请考虑std::shared_ptr<>
,该CC_19将允许多个对象共享所有权,但请确保无法进行循环引用。
除此之外
一项肤浅的检查表明,"状态"可能拥有其Transition
S,因为总的来说,在国家存在的同时,它们在存在时就会有意义,而在不存在的情况下就停止变得有意义。因此,继续使用vector<std::unique_ptr<> >
并访问State
和/或Transition
作为指针。
如果在您的情况下不起作用,则可能需要一个拥有所有状态和所有过渡的"拟合"上下文对象被摧毁。
// This line causes compiler errors states.push_back(nstate);
nstate
对象是State
类的实例。State
类包含两个数据成员:int
(可复制(和unique_ptr
s的vector
,即可移动但不可复制(由于unique_ptr
是可移动的,但不可复制(。因此,整个State
类都是可移动的,但不可复制。因此,您必须std::move
nstate
对象到states
向量:
states.push_back(std::move(nstate));
如果您想要 copy 语义,则应使用 shared_ptr
s的向量(参考计数 smart smart指针,并且是 as em 可复制可移动(。
我还将对您的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中引入(,而不是用明确调用new
返回的RAW指针构造std::unique_ptr
。
您应该避免使用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{}});
}
}
您需要为您的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));
您将std::unique_ptr<Transition>
传递给按值函数,该值应在void add_transition(std::unique_ptr<Transition> trans)
中创建本地副本。
如果您将通过参考std::unique_ptr<Transition>& trans
传递值,则不需要任何std::move
add_transition
函数。
您也可能需要使用std::make_unique<Transition>()
而不是std::uniqye_ptr<Transition>(new Transition())
。
new
关键字的封装使您的代码更清晰,并命令创建内存泄漏的概率。