如何解决防止大型结构之间不必要的复制 复制危害1:getData()以外的建筑复制危险2:data和data2
我有巨大的结构DataFrom和Data(实际上它们具有不同的成员)。数据是从DataFrom创建的。
#For Class 1
(0.85-0)/((0.85-0.45)/47) + (47 - 0)
#For Class 2
(1.98-1)/((1.98-0.85)/150) + (157 - 47)
#For Class 3
(5.20-3)/((5.2-1.98)/173) + (173 - 150)
#Final
Age<-((0.85-0)/((0.85-0.45)/47) + (47 - 0) +
(1.98-1)/((1.98-0.85)/150) + (157 - 47) +
(5.20-3)/((5.2-1.98)/173) + (173 - 150))/365
Age
#[1] 1.44702
# Desirable output
new.df
# IDtest Class Day Area Age
#1 1 1 0 0.45 1.44702
#2 1 1 47 0.85 1.44702
#3 1 2 76 1.50 1.44702
#4 1 2 100 1.53 1.44702
#5 1 2 150 1.98 1.44702
#6 1 3 173 5.20 1.44702
由于数据量很大,因此在getData函数中有整个数据结构的副本。能以某种优雅的方式防止这种情况发生吗?
我有一个想法:
struct DataFrom{
int a = 1;
int b = 2;
};
static DataFrom dataFrom;
struct Data{
int a;
int b;
};
class DataHandler{
public:
static Data getData(const DataFrom& data2){
Data data;
setA(data,data2);
setB(data,data2);
return data;
}
private:
static void setA(Data& dest,const DataFrom& source){
dest.a = source.a;
}
static void setB(Data& dest,const DataFrom& source){
dest.b = source.b;
}
};
int main(){
auto data = DataHandler2::getData(dataFrom); // copy of whole Data structure
// ...
return 0;
}
但是我更愿意将数据作为返回值而不是输出参数来获取。
解决方法
这里有两种潜在的“复制危害”:
复制危害1:getData()
以外的建筑
在main()
的第一行中,您注释了“整个数据结构的副本”-正如注释者所指出的,由于命名返回值优化,该结构实际上不会被复制,或简称NRVO。您可以在几年前的一篇不错的博客文章中了解到它:
Fluent{C++}: Return value optimizations
简而言之:编译器对其进行了排列,以使data
函数内的getData
从main()
调用时实际上是main中data
的别名
复制危险2:data
和data2
第二个“复制恐慌”与setA()
和setB()
一起使用。在这里,您必须更加主动,因为在同一函数中确实有两个有效的实时结构-data
中的data2
和getData()
。实际上,如果Data
和DataFrom
只是大型结构-那么您将以编写代码的方式从data2
到data
进行大量复制。>
将语义转移到救援中
但是,如果您的DataFrom
拥有对某些已分配存储的引用,例如std::vector<int> a
而不是int[10000] a
-您可以从{{ 1}}(而不是从中复制)-通过使DataFrom
带有签名getData()
。了解更多关于搬到这里的信息:
在我的示例中,这意味着您现在将static Data getData(DataFrom&& data2)
的原始缓冲区用作data2.a
-无需将该缓冲区的内容复制到其他任何地方。但这意味着之后您将无法再使用data
,因为它的data2
字段已被蚕食,从此移走了。
...或只是“偷懒”。
您可以尝试其他方法,而不是基于移动的方法。假设您定义了以下内容:
a
现在class Data {
protected:
DataFrom& source_;
public:
int& a() { return source_.a; }
int& b() { return source_.b; }
public:
Data(DataFrom& source) : source_(source) { }
Data(Data& other) : source_(other.source) { }
// copy constructor?
// assignment operators?
};
不是一个简单的结构;它更像是Data
的外观(也许还有其他一些字段和方法)。这样做不太方便,但是好处是您现在创建了一个DataFrom
,只引用了Data
,没有任何其他内容的副本。在 access 上,您可能需要取消引用指针。
其他说明:
-
您的
DataFrom
被定义为一个类,但是看起来它只是一个命名空间。您永远不会实例化“数据处理程序”。考虑阅读: -
我的建议不涉及任何C ++ 17。移动语义是在C ++ 11中引入的,如果您选择“惰性”方法-即使在C ++ 98中也可以使用。
由于您已经用c++17标记了此代码,因此您可以以防止发生任何复制(或能够进行复制)的方式编写代码,并且如果编译成功,您将知道静态将不会进行复制。
C ++ 17保证从函数返回时可以避免复制,这可以确保在某些情况下不进行任何复制。您可以通过更改代码来确保这一点,以使Data
具有一个= delete
d复制构造函数,并更改getData
以返回一个构造对象。如果代码完全可以正确编译,则可以确保没有任何复制发生(因为复制会引发编译错误)
#include <iostream>
struct DataFrom{
int a = 1;
int b = 2;
};
static DataFrom dataFrom;
struct Data{
Data() = default;
Data(const Data&) = delete; // No copy
int a;
int b;
};
class DataHandler{
public:
static Data getData(const DataFrom& data2){
// construct it during return
return Data{data2.a,data2.b};
}
private:
static void setA(Data& dest,const DataFrom& source){
dest.a = source.a;
}
static void setB(Data& dest,const DataFrom& source){
dest.b = source.b;
}
};
int main(){
auto data = DataHandler::getData(dataFrom); // copy of whole Data structure
return 0;
}
此文件将编译,没有任何多余的副本-您可以here on compiler explorer看到它
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。