我最近在与Bjarne Stoustrup的一次演讲中谈到了c ++ 11以及为什么它有意义.
他令人敬畏的一个例子是移动构造者的新闻'&&'符号.
然后我想回家,开始思考,"我什么时候需要这样的东西?"
我的第一个例子是下面的代码:
class Number {
private:
int value;
public:
Number(const int value) : value(value){
cout << "Build Constructor on " << value << endl;
}
Number(const Number& orig) : value(orig.value){
cout << "Copy Constructor on " << value << endl;
}
virtual ~Number(){}
int toInt() const{
return value;
}
friend const Number operator+(const Number& n0, const Number& n1);
};
const Number operator+(const Number& n0, const Number& n1){
return Number(n0.value + n1.value);
}
int main(int argc, char** argv) {
const Number n3 = (Number(2) + Number(1));
cout << n3.toInt() << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这段代码正是移动构造函数应该解决的问题.n3变量是从对'+'运算符返回的值的引用构造的.
除了这是运行代码的输出:
Build Constructor on 1
Build Constructor on 2
Build Constructor on 3
3
RUN SUCCESSFUL
Run Code Online (Sandbox Code Playgroud)
输出显示的是复制构造函数永远不会被调用 - 这是关闭优化的.我很难扭转代码的一部分,足以让它运行副本construtor.将结果包装在std :: pair中就可以了,但这让我想到了.
运算符算术中的move-constructors的参数实际上是一个失败的参数吗?
为什么我的拷贝构造函数被调用,为什么它被调用:
using namespace std;
class Number {
private:
int value;
public:
Number(const int value) : value(value){
cout << "Build Constructor on " << value << endl;
}
Number(const Number& orig) : value(orig.value){
cout << "Copy Constructor on " << value << endl;
}
virtual ~Number(){}
int toInt() const{
return value;
}
friend const std::pair<const Number, const Number> operator+(const Number& n0, const Number& n1);
};
const std::pair<const Number, const Number> operator+(const Number& n0, const Number& n1){
return make_pair(Number(n0.value + n1.value), n0);
}
int main(int argc, char** argv) {
const Number n3 = (Number(2) + Number(1)).first;
cout << n3.toInt() << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
随着输出:
Build Constructor on 1
Build Constructor on 2
Copy Constructor on 2
Build Constructor on 3
Copy Constructor on 3
Copy Constructor on 2
Copy Constructor on 3
Copy Constructor on 2
Copy Constructor on 3
3
RUN SUCCESSFUL
Run Code Online (Sandbox Code Playgroud)
我想知道逻辑是什么以及为什么这对操作员基本上搞砸了性能?
更新:
我做了另一个修改,发现如果我make_pair用该对的实际模板化构造函数替换,pair<const Number, const Number>这减少了复制构造函数被触发的次数:
class Number {
private:
int value;
public:
Number(const int value) : value(value){
cout << "Build Constructor on " << value << endl;
}
Number(const Number& orig) : value(orig.value){
cout << "Copy Constructor on " << value << endl;
}
virtual ~Number(){}
int toInt() const{
return value;
}
friend const std::pair<const Number, const Number> operator+(const Number& n0, const Number& n1);
};
const std::pair<const Number, const Number> operator+(const Number& n0, const Number& n1){
return std::pair<const Number, const Number>(Number(n0.value + n1.value), n0);
}
int main(int argc, char** argv) {
const Number n3 = (Number(2) + Number(1)).first;
cout << n3.toInt() << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
Build Constructor on 1
Build Constructor on 2
Build Constructor on 3
Copy Constructor on 3
Copy Constructor on 2
Copy Constructor on 3
3
RUN SUCCESSFUL
Run Code Online (Sandbox Code Playgroud)
所以它的使用make_pair是有害的吗?
Nic*_*las 14
考虑一下这个简单的C++代码:
class StringHolder
{
std::string member;
public:
StringHolder(const std::string &newMember) : member(newMember) {}
};
std::string value = "I am a string that will probably be heap-allocated.";
StringHolder hold(value);
Run Code Online (Sandbox Code Playgroud)
执行第二行后,存在多少个字符串副本?答案是两个:一个存储value,一个存储hold.那很好......有时候.通常有时候你想给某人一个字符串的副本,同时为自己保留.但有些时候你也不想这样做.例如:
StringHolder hold("I am a string that will probably be heap-allocated.");
Run Code Online (Sandbox Code Playgroud)
这将创建一个std::string临时的,然后将其传递给StringHolder构造函数.构造函数将复制构造其成员.构造函数完成后,临时文件将被销毁.有一次,我们有两个字符串副本,无论如何.
没有必要有两个字符串副本.我们想要做的是移动的std::string参数为StringHolder,使有永远只能一个字符串的副本.
这就是搬家建设的地方.
A std::string基本上只是指向已分配的字符数组的指针的包装器,以及包含该数组长度的大小(以及容量,但现在不用担心).如果你有一个std::string,并且你想将它移动到另一个,那么新字符串必须声明所分配的字符数组的所有权,旧字符串必须放弃所有权.在C++ 03中,您可以通过swap操作执行此操作:
std::string oldStr = "I am a string that will probably be heap-allocated.";
std::string newStr;
std::swap(newStr, oldStr);
Run Code Online (Sandbox Code Playgroud)
这将内容移动oldStr到newStr没有任何内存分配.
C++ 11的移动语法提供了两个不重要的特性std::swap.
首先,移动可以隐式发生(但只有在这样做时才安全).swap如果要交换,必须明确调用; 通过编写自然代码可以实现移动.例如,StringHolder从以前开始,做一个改变:
class StringHolder
{
std::string member;
public:
StringHolder(std::string newMember) : member(std::move(newMember)) {}
};
StringHolder hold("I am a string that will probably be heap-allocated.");
Run Code Online (Sandbox Code Playgroud)
创建了多少个这个字符串的副本?答案是......只有一个:临时的建设.因为它是一个临时的,C++ 11足够聪明,知道它可以移动构造由它初始化的任何东西.因此它移动构造构造函数的value参数StringHolder(或者更可能完全构造构造).这会将存储的内存从临时移动到newMember.所以不会发生复制.
之后,我们在构造时显式调用move构造函数member.这再次将分配的内存从移动newMember到member.
我们只有不断分配一个字符串一次.这可以大大节省性能.
现在,这与您自己类型的构造函数有什么关系?好吧,考虑一下这段代码:
class StringHolder
{
std::string member;
public:
StringHolder(std::string newMember) : member(std::move(newMember)) {}
StringHolder(const StringHolder &old) : member(old.member) {}
StringHolder(StringHolder &&old) : member(std::move(old.member)) {}
};
StringHolder oldHold = std::string("I am a string that will probably be heap-allocated.");
StringHolder newHold(oldHold);
Run Code Online (Sandbox Code Playgroud)
这一次,我们现在有一个带有复制和移动构造函数的类.我们得到多少份字符串?
二.当然是两个.我们有,oldHold并且newHold每个都有一个字符串的副本.
但是,如果我们这样做:
StringHolder oldHold = std::string("I am a string that will probably be heap-allocated.");
StringHolder newHold(std::move(oldHold));
Run Code Online (Sandbox Code Playgroud)
然后再次只有一个字符串的副本.
这就是为什么运动很重要.这就是重要的原因:它减少了你可能需要躺着的东西的副本数量.
为什么我的拷贝构造函数没有被调用
您的复制构造函数未被调用,因为它被省略了.它正在进行返回值优化.关闭优化并没有帮助,因为大多数编译器无论如何都会畏缩.没有理由不在何时可以进行省略.
对于函数返回值,在无法进行省略的情况下,移动很重要.
| 归档时间: |
|
| 查看次数: |
1335 次 |
| 最近记录: |