[英]Should I use pointers or move semantics for passing big chunks of data?
我对推荐的编码技术有疑问。 我有一个模型分析工具,我有时需要传递大量数据(从工厂类到拥有多个异构块的工厂类)。
我的问题是,是否应该使用指针或移动所有权是否存在一些共识(我需要尽可能避免复制,因为数据块的大小可能大到1 GB)。
指针版本如下所示:
class FactoryClass {
...
public:
static Data * createData() {
Data * data = new Data;
...
return data;
}
};
class StorageClass {
unique_ptr<Data> data_ptr;
...
public:
void setData(Data * _data_ptr) {
data_ptr.reset(_data_ptr);
}
};
void pass() {
Data * data = FactoryClass::createData();
...
StorageClass storage;
storage.setData(data);
}
移动版本是这样的:
class FactoryClass {
...
public:
static Data createData() {
Data data;
...
return data;
}
};
class StorageClass {
Data data;
...
public:
void setData(Data _data) {
data = move(_data);
}
};
void pass() {
Data data = FactoryClass::createData();
...
StorageClass storage;
storage.setData(move(data));
}
我更喜欢移动版本 - 是的,我需要将移动命令添加到主代码中,但最后我只有存储中的对象,我不再需要关心指针语义。
但是,在使用我不详细了解的移动语义时,我并不是很放松。 (我不关心C ++ 11的要求,因为代码已经只有Gcc4.7 +可编译)。
有人会有一个支持任何一个版本的引用吗? 或者是否有其他一些首选数据如何传递数据?
由于关键字通常会导致其他主题,因此我无法使用Google。
谢谢。
编辑注意:第二个例子被重构以结合评论中的建议,语义保持不变。
当您将对象传递给函数时,您传递的内容部分取决于该函数将如何使用它。 函数可以通过以下三种方式之一使用对象:
它可以在函数调用的持续时间内简单地引用该对象,其中调用函数(或者它最终为调用堆栈的父对象)维护对象的所有权。 在这种情况下,引用可以是常量引用或可修改引用。 该函数不会长期存储此对象。
它可以直接复制对象。 它没有获得原件的所有权,但它确实获得了原件的副本,以便存储,修改或复制它的内容。 请注意,#1与此之间的区别在于复制在参数列表中显式化。 例如,按值获取std::string
。 但这也可以像通过值获取int
一样简单。
它可以获得对象的某种形式的所有权 。 然后函数对对象的破坏负有一定的责任。 这也允许函数长期存储对象。
我对这些范例的参数类型的一般建议如下:
尽可能通过明确的语言参考来获取对象。 如果那不可能,请尝试使用std::reference_wrapper
。 如果这不起作用,并且没有其他解决方案似乎合理, 那么使用指针。 一个指针可以用于像可选参数这样的东西(尽管C ++ 14的std :: optional会使它不那么有用。指针仍然会有用),语言数组(尽管如此,我们还有一些对象涵盖了这些参数的大部分用途) ),等等。
按值获取对象。 那个人非常不容谈判。
通过value-move(即:将其移动到by-value参数)或通过指向对象的智能指针获取对象(也将通过值获取,因为您无论如何都要复制/移动它) 。 您的代码的问题是您通过指针转移所有权,但使用原始指针 。 原始指针没有所有权语义。 在分配任何指针的那一刻,你应该立即将它包装在某种智能指针中。 所以你的工厂函数应该返回一个unique_ptr
。
你的案子似乎是#3。 您在值移动和智能指针之间使用的完全取决于您。 如果由于某种原因必须堆分配Data
,那么几乎可以选择。 如果Data
可以进行堆栈分配,那么您有一些选择。
我通常会根据对Data
内部大小的估计来做到这一点。 如果在内部,它只是几个指针/整数(和“少数”,我的意思是3-4),然后把它放在堆栈上是好的。
实际上,它可以更好,因为你将有更少的机会出现双缓存错过。 如果您的Data
函数通常只是从另一个指针访问数据,如果您通过指针存储Data
,那么对它的每个函数调用都必须取消引用您存储的指针以获取内部指针,然后取消引用内部指针。 这是两个潜在的缓存未命中,因为两个指针都没有与StorageClass
任何位置。
如果按值存储Data
,则Data
的内部指针很可能已经存在于缓存中。 它与StorageClass
的其他成员有更好的位置; 如果您之前访问过一些StorageClass
,那么您已经为缓存未命中付了代价,因此您可能已经在缓存中拥有了Data
。
但运动不是免费的。 它比完整副本便宜,但它不是免费的 。 您仍然在复制内部数据(并且可能会使原始数据上的任何指针无效)。 但话说回来,在堆上分配内存也不是免费的。 也没有解除它。
但话又说回来,如果你不经常移动它(你移动它到达它的最终位置,但在那之后再多一点),即使移动一个更大的物体也没关系。 如果你使用它比移动它更多,那么对象存储的缓存局部性可能会胜过移动的成本。
最终选择其中一个并不是很多技术原因。 我会说在合理的情况下默认运动。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.