c++ - 我可以在 std::move 之后重新使用像 std::vector 这样的复杂类吗?
问题描述
我正在创建一个游戏,并尝试学习移动语义/r 值引用。
我有一个类,可以在每一帧中添加Event
一个vector
。
60 帧后,我想将所有累积的事件移动到一个新类(称为Step
)中,该类将存储在另一个vector
.
我希望重复此过程,因此在Event
移动矢量后,它应该重置为空。
#include <vector>
class Event
{
...
}
class Step
{
std::vector<Event> Events;
Step(std::vector<Event>&& InEvents) : Events {InEvents} {}
}
class EventAccumulator
{
std::vector<Event> Events;
std::vector<Step> Steps;
void Update(int FrameCount)
{
Events.push_back(Event());
if (FrameCount % 60 == 0)
{
// Move accumulated events into a new Step.
Step NewStep = Step(std::move(Events));
Steps.push_back(NewStep);
// Reset Events so that we can accumulate future events.
Events = std::vector<Event>();
}
}
}
// Game Loop
int GlobalFrameCount = 0;
EventAccumulator eventAccumulator{};
while (true)
{
eventAccumulator.Update(GlobalFrameCount++);
}
据我了解,该行将Step NewStep = Step(std::move(Events));
“给予” Events
(NewStep
即不复制向量)。如果我在这方面错了,请纠正我。
我希望该行被Events = std::vector();
重置EventAccumulator.Events
为空向量,但我不想NewStep.Events
被重置。
我的问题是,这段代码会做我想要的吗?
此外,您如何判断这是否适用于所有复杂的类,例如std::vector
?我认为这是由类的赋值运算符重载决定的,但我对此感到困惑。
解决方案
您的代码总是复制Events
向量,因此Events = std::vector();
只会删除复制的元素。
为什么它复制而不是移动?我们来看看Step
构造函数:
// that's a copy -----v
Step(std::vector<Event>&& InEvents) : Events {InEvents} {}
事实上,表达式(InEvents)
是一个左值,因为它有一个名字。您必须使用以下方法将其转换为右值std::move
:
Step(std::vector<Event>&& InEvents) : Events {std::move(InEvents)} {}
通过 获取参数时&&
,请记住这是一个可能的移动,因为它只是一个引用,就像任何其他引用一样,您必须明确移动它。
此行不会编译:
Events = std::vector();
也许你的意思是:
Events = {};
这确实可以让你重用你的向量。它将以导致确定状态的方式重置。
我希望该行被
Events = std::vector();
重置EventAccumulator.Events
为空向量,但我不想NewStep.Events
被重置。我的问题是,这段代码会做我想要的吗?
C++ 具有值语义。除非NewStep
包含 a ,否则std::vector<Event>&
您不能影响其他地方的变量。您还可以移动在 中构造向量NewStep
。这应该告诉你一些事情:你构建了一个新的向量。无论您如何改变旧向量,它都不会影响不同的向量。Step
如果您更正构造函数,此代码将执行您想要的操作。
请记住,如果您想避免多次分配,则必须这样调用reserve
:
Events.reserve(60);
正如我在评论中添加的那样,构造函数是移动语义的一种特殊情况:按值获取并移动它会增加很少的成本,并且很可能会被省略。这就是我的意思:
Step(std::vector<Event> InEvents) : Events {std::move(InEvents)} {}
如果您通过副本传递,那么它将复制InEvents
并移动它。如果你通过 move 传递,它会调用 move 构造函数两次,没有副本。
由于调用移动构造函数的成本可以忽略不计,它使您免于编写重载。
它只适用于构造函数,因为我们无论如何都不能重用容量。这不适用于赋值或 setter 函数。
推荐阅读
- caching - 设置关联缓存 - 有多少内存块映射到一组?
- ubuntu - Ubuntu - 使用用户输入的顺序文件命名
- c++ - 当我使用 1000 个键值的数据集时出现分段错误,并且由于某种原因,AVL 旋转不会发生在节点周围
- sas - 将数据字典拆分为输入/格式/标签文件时,SAS 导出文件是否有规范?
- python - Raspberry Pi 3 GUI 界面按钮和 LED 灯
- microsoft-graph-api - 扩展和过滤 MS Graph API 不起作用
- spatstat - 在 spatstat 中为锥形点云创建 pp3 点模式
- javascript - 如何修复 phpmyadmin 中的日期错误 0000-00-00?
- c - 使用 GLUT 和 C 编写文本
- javascript - html中的外部javascript文件不起作用