#include <iostream>
using namespace std;
#include <cstring>
class Word{
private:
char* ptr = nullptr;
public:
Word(){
cout << "default constructor" << endl;
}
Word(const char* sentence):ptr{new char [strlen(sentence)+1]}{
strcpy(ptr, sentence);
cout << "conversion constructor: " << ptr << endl;
}
Word(const Word& w):ptr{new char [strlen(w.ptr)+1]}{
strcpy(ptr, w.ptr);
cout << "copy constructor: "<< ptr << endl;
}
~Word(){
cout << "destructor: " << ptr << endl;
}
};
int main(){
Word a ("A stands for apple!");
Word&& b = "B stands for Banana, rvalue ref";
b = a;
}
我的Eclipse结果:
conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
destructor: A stands for apple!
destructor: A stands for apple!
My Extectation:
conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
destructor: B stands for Banana, rvalue ref
destructor: A stands for apple!
destructor: A stands for apple!
我对这一步感到困惑。
b = a;
当a被分配给b时,它可以假设首先破坏b所持有的临时对象(cstring为“B代表Banana,rvalue ref”),然后将a的值赋值给b。为什么在Eclipse的结果中,它不会执行临时对象的破坏?
答案 0 :(得分:4)
你的期望是错误的。没有比建筑物更多的破坏。
当a被分配给b时,它可以假设首先破坏临时对象
没有。 b
指的是临时对象。分配给对象不会导致对象被销毁。
会发生什么:隐式生成的Word
赋值运算符将分配所有成员。因此,在分配之后,b.ptr
的先前值被泄露并且具有与a.ptr
相同的值(指向相同的字符串)。
答案 1 :(得分:0)
b = a;
正在调用赋值运算符,而不是复制构造函数。如果明确删除,代码将不会编译:
// trimmed...
Word(const Word& w):ptr{new char [strlen(w.ptr)+1]}{
strcpy(ptr, w.ptr);
cout << "copy constructor: "<< ptr << endl;
}
Word& operator = (const Word& w) = delete;
编译行:
$ g++ rvalue-ref.cpp -o rvalue-ref
rvalue-ref.cpp: In function ‘int main()’:
rvalue-ref.cpp:45:9: error: use of deleted function ‘Word& Word::operator=(const Word&)’
b = a;
^
rvalue-ref.cpp:20:15: note: declared here
Word& operator = (const Word& w) = delete;
^~~~~~~~
编译器将提供默认赋值运算符,因此您的问题中的代码可以利用它。要查看发生了什么,请添加复制分配和移动分配运算符。
Word& operator = (const Word& w) {
auto temp = new char [strlen(w.ptr)+1];
strcpy(temp, w.ptr);
delete [] ptr;
ptr = temp;
cout << "assignment operator: " << ptr << endl;
return *this;
}
Word& operator = (Word&& w) {
std::swap(ptr, w.ptr);
cout << "swap operator: " << ptr << endl;
return *this;
}
有了这些,我得到了预期的输出:
conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
assignment operator: A stands for apple!
destructor: A stands for apple!
destructor: A stands for apple!
顺便说一句,你会泄漏记忆。你的析构函数应该是这样的:
~Word(){
cout << "destructor: " << ptr << endl;
delete [] ptr;
}
$ valgrind ./rvalue-ref
==10736== Memcheck, a memory error detector
==10736== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10736== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10736== Command: ./rvalue-ref
==10736==
conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
assignment operator: A stands for apple!
destructor: A stands for apple!
destructor: A stands for apple!
==10736==
==10736== HEAP SUMMARY:
==10736== in use at exit: 0 bytes in 0 blocks
==10736== total heap usage: 5 allocs, 5 frees, 73,800 bytes allocated
==10736==
==10736== All heap blocks were freed -- no leaks are possible
==10736==
==10736== For counts of detected and suppressed errors, rerun with: -v
==10736== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
您还可以使用复制/交换习惯用法(下面)实现赋值运算符。这将添加一个额外的构造函数/析构函数输出,因为它是一个临时的,但总的来说是一个很好的实践。
Word& operator = (const Word& w) {
Word temp(w);
std::swap(ptr, temp.ptr);
cout << "assignment operator: " << ptr << endl;
return *this;
}