假设我有以下结构:
struct Test
{
int n1; // represents POD types, in real program
int n2; // I have much more data
char str[STR_SIZE];
vector<int> v; // represents non-POD types
};
现在我向它添加移动语义:
struct Test
{
int n1;
int n2;
char str[STR_SIZE];
vector<int> v;
Test(){}
Test(const Test& other) : v(other.v)
{
Copy(other);
}
Test(Test&& other) : v(move(other.v))
{
Copy(other);
}
Test& operator=(const Test& other)
{
if ( this != &other )
{
Copy(other); v = other.v;
}
return *this;
}
Test& operator=(Test&& other)
{
if ( this != &other )
{
Copy(other); v = move(other.v);
}
return *this;
}
private:
void Copy(const Test& other) // copy POD members
{
n1 = other.n1; n2 = other.n2;
memcpy(str, other.str, sizeof(str));
}
};
我对Copy
函数的性能不满意,该函数指定所有非POD结构成员。是否有更好的方法来进行这种转变?我需要获得最大的性能(这种结构保存在实际程序中的STL容器中)和标准符合性。我只能考虑将所有POD成员放在开头,并使用单memcpy
次调用而不是Copy
函数复制它们,但这看起来像是黑客。
目前我正在使用VS2010编译器。
答案 0 :(得分:1)
您可以将所有POD成员合并到一个结构中并使用pimpl idiom,这样它们也可以一起移动。
答案 1 :(得分:0)
move
语义在有效移动状态时是有效的。
这是一种循环,但是如果您的状态是结构中“本地”拥有的大量二进制数据,则移动需要在目标结构中复制此二进制数据。
std::vector
本地不拥有大量二进制数据。相反,它拥有一个连续的堆分配缓冲区。此缓冲区可以更改所有权,因此move
语义非常有效。
move
语义也很有用。当move
std::shared_ptr
时,它可以避免对引用计数数据进行原子递增/递减。这可以防止争用(所有原子操作导致争用),增加局部性(无堆访问),即使它修改更多数据而不是copy
,也是值得的。
如果您需要高效的move
语义,请将大量二进制数据移动到堆中。轻松完成此操作的一种方法是存储std::unique_ptr<POD_data>
并在请求副本时手动复制它(需要堆分配)。另一种方法是std::shared_ptr<POD_data>
,并且具有某种写时复制访问技术。
但请注意,许多类型的数据实际上并不需要复制语义。实现std::unique_ptr<POD_data>
版本,阻止复制构造函数。提供一种方法MakeCopy
,可以为您实际想要复制大块内存的罕见情况生成副本。这样可以轻松有效地使用您的结构,并且使用效率低,难以追踪。
您必须通过条件保护对POD_data
的访问权限,或接受move
d-from版本或数据的堆分配。我建议一个返回POD_data&
的方法来检查unique_ptr
是否有效,如果没有,则分配一个新的POD_data
:可预测的分支不会是一个重大的性能影响,并且如果用户连续多次使用它,则可以缓存POD_data&
。
如果POD_data
中的内存足够大以至于它周围的内存是昂贵的,那么通过指针访问它的成本通常是适度的,而不必复制它的节省可能是显着的。