我需要创建一个大小为 1024 的结构列表。因此,我尝试通过使用移动语义来优化对列表的推送操作。但移动语义仅对堆分配的内存有意义(据我所知)。有人可以建议我更好的方法吗?这是我的代码
#include <iostream>
#include <list>
struct St {
int32_t arr[1024];
};
int main() {
std::list<St> struct_list;
for(int32_t i = 0; i < 1024; ++i) {
St st; // stack allocated and memory gets deallocated after each loop
std::cout << &st << std::endl;
struct_list.emplace_back(std::move(st)); // I feel std::move doesn't make any sense here even if I implement move constructor, still data gets copied into the std::list which is allocated in heap, so not efficient.
std::cout << &struct_list.back() << "\n" << std::endl;
}
return EXIT_SUCCESS;
}
鉴于您当前对
St
的定义:
struct St {
int32_t arr[1024];
};
从技术上讲,对于
St
的这个特定定义,移动和复制两个结果是相同的。
但是,从语义上讲,在完成后标记
st
进行移动确实有意义,并且 st
无论如何都会被销毁。例如,如果您稍后决定将 St
的定义更改为:
struct St {
std::vector<int32_t> vec;
};
然后,它会产生影响 - 矢量数据成员将被移动而不是复制。
简而言之,我的建议是关注移动操作的语义 – 即,对于要移动的对象有意义吗?好吧,如果你无论如何都要处理该对象,那么它非常可能会。
如果您想升级到C++17,您可以使用emplace_back的返回值首先将其添加到列表中,然后填充它。 您可以(如果默认可构造,如您的示例)首先 emplace_back 一个新实例,然后使用
.back()
获取它并填充它。
堆栈分配和释放并不昂贵,尤其是对于 POD 而言。重要的是你对它所做的逻辑。
关于移动,对于这个例子,std::move 的作用与复制相同,所以不,你不需要它。但是,如果这是一个
std::string
数组,则 std::move 将允许您将字符串从一个数组移动到另一个数组。
一般来说,堆栈对象可以受益于 std::move() 的使用。正如答案指出的那样,因为 St 包含删除 St 时删除的数组,所以移动构造函数无济于事。然而,例如,包含向量的对象可以使用移动赋值(或构造,当然这比复制多个 1k 数组要好得多)。
我测试了以下示例:
struct St
{
int32_t arr[1024];
};
struct StVect
{
StVect(const std::initializer_list<St> values)
{
int i = 0;
for (const auto o : values) { st.emplace_back(o); }
}
std::vector<St> st;
};
void OutputAFewValues(const StVect& st_vect)
{
std::cout << "\n";
std::cout << st_vect.st[0].arr[0] << " ";
std::cout << st_vect.st[0].arr[1] << " ";
std::cout << st_vect.st[0].arr[2] << " ";
std::cout << st_vect.st[1].arr[0] << " ";
std::cout << st_vect.st[1].arr[1] << " ";
std::cout << st_vect.st[1].arr[2] << " ";
std::cout << "\n";
}
auto test_move_st() -> void
{
StVect st_vect{{0, 1, 3, 5, 7}, {10, 11, 13, 15, 17}};
StVect st_vect2{{0, 2, 4, 6, 8}, {10, 12, 14, 16, 18}};
std::cout << "\n st_vect...\n";
OutputAFewValues(st_vect);
OutputAFewValues(st_vect2);
// Moves the addresses of each struct St in the vector as
// opposed to copying each St and its array data.
st_vect2 = std::move(st_vect);
std::cout << "\n st_vect2...\n";
OutputAFewValues(st_vect2);
}
对于您的具体示例,如果您想从移动中受益,那么您可以将结构保留在堆栈上,但数组应该位于堆上。这样,定位可以复制地址而不是数据。
struct st_movable
{
static constexpr size_t ArraySize = 1024;
std::unique_ptr<int32_t[]> arr{new int32_t[ArraySize]};
};