我实现了State模式,该模式还包括对主题类的引用。
class State {
public:
virtual void doStuff() = 0;
protected:
State(Subject& s) : subject_{s} {}
private:
Subject& subject_;
};
class StateA : public State {
public:
StateA(Subject& s, Subject& target) : State(s), target_{t} {}
void doStuff() override { /* implementation requiring subject_ */ }
private:
Subject& target_;
};
class Subject {
public:
void doStuff() { state_->doStuff(); }
State* state_;
};
当我想在需要移动语义的容器中使用一组主题时(例如std::vector
),默认的move-constructor不够用,因为移动状态仍然引用旧主题。在一种情况下,国家甚至需要另外一个主题,而该主题在移动时会导致无效的引用。
如何在此设置中实施适当的移动语义?目前,我在开始时要预留足够的空间,因此不需要移动,但是将来可能不可行。
答案 0 :(得分:1)
由于引用不能在C ++中重新绑定,因此不能正确地重新分配带有引用成员的类的任何实例,除非对象从引用中分配的对象与分配给对象的对象相同。但是,由于您希望能够移动Subject
,因此您需要能够重新分配这些成员。这里有两个选项:您可以为subject_
和target_
成员使用指针,也可以使用std::reference_wrapper
。
我个人更喜欢std::reference_wrapper
,因为对指针不熟悉的人可能会以为它可能是nullptr
,而reference_wrapper
却清楚地表明引用始终是有效的。但是,与指针相反,std::reference_wrapper
要求引用的类型在C ++ 20之前是完整类型,因此仅向前声明是不够的,您需要交换State
和{您必须在代码中使用{1}}才能使用它(如您在此答案的最后看到的那样)。
使用Subject
会将您的std::reference_wrapper
类更改为类似的内容(请注意,我还在State
的构造函数中添加了Subject::state_
缺少的赋值):
State
但是正如您还指出的那样,在移动class State {
public:
State(Subject& s)
: subject_{std::ref(s)} {
s.state_ = this;
}
State(State const&) = delete;
State(State&&) = delete;
State& operator=(State const&) = delete;
State& operator=(State&&) = delete;
virtual ~State() = default;
virtual void doStuff() = 0;
protected:
Subject& subject() {
return subject_;
}
private:
std::reference_wrapper<Subject> subject_;
};
class StateA : public State {
public:
StateA(Subject& s, Subject& target)
: State(s),
target_{std::ref(target)} {
}
void doStuff() override { /* implementation requiring subject() */ }
private:
Subject& target() {
return target_;
}
std::reference_wrapper<Subject> target_;
};
时,您需要通知Subject
对象State
的移动是为了调整当前悬空的参考点{{1 }}。但是,您不仅需要通知Subject
类来重新分配subject_
成员,而且State
类还需要在{ {1}}已移动。
由于我假设您不想引入耦合,因此subject_
需要了解所有StateA
子类,这些子类像{{1}一样具有对target_
的附加引用}因此,我们需要一个通用的通知机制,以便Subject
的具体(子)类可以重新分配适当的Subject
成员。我的想法是让State
注册一个Subject
在移动时调用的回调。为此,我将使用StateA
。这会将State
类更改为以下内容:
reference_wrapper
当然,我们还需要修改State
和Subject
构造函数以注册正确的回调:
std::function
重新排列所有内容使其可以编译后,我们最终得到
Subject