简体   繁体   English

将左值分配给右值引用时会发生什么? 没有破坏临时对象?

[英]What happen when a lvalue assigned to a rvalue reference? No destruction of the temporary object?

#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;
}

My Eclipse result: 我的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!

I am confused by this step. 我对这一步感到困惑。

b = a;

When a is assigned to b, it could suppose to first destruct the temporary object (with cstring of "B stands for Banana, rvalue ref") that b is holding, then assign the value of a to b. 将a分配给b时,可以假设首先销毁b持有的临时对象(cstring为“ B代表Banana,rvalue ref”),然后将a的值分配给b。 Why in the Eclipse's result, it does not perform the destruction of the temporary object? 为什么在Eclipse的结果中,它不执行对临时对象的销毁?

Your expectation is wrong. 您的期望是错误的。 There can't be more destructions than there are constructions. 破坏不可能超过建筑。

When a is assigned to b, it could suppose to first destruct the temporary object 将a分配给b时,它可能会假设首先破坏临时对象

No. b refers to the temporary object. b号指的是临时对象。 Assigning to an object doesn't cause the object to be destroyed. 分配给对象不会导致对象被破坏。

What happens is: The implicitly generated assignment operator of Word will assign all members. 发生的事情是: Word隐式生成的赋值运算符将分配所有成员。 So, after the assignment the previous value of b.ptr is leaked and has the same value (points to same string) as a.ptr . 因此,分配后的前值b.ptr被泄露,并具有相同的值(指向相同的字符串)作为a.ptr

b = a; is invoking the assignment operator, not the copy constructor. 正在调用赋值运算符,而不是副本构造函数。 If explicitly deleted, the code won't compile: 如果明确删除,则代码将无法编译:

// 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;

The compilation line: 编译行:

$ 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;
               ^~~~~~~~

The compiler will provide a default assignment operator, so the code in your question is leverage that. 编译器将提供一个默认的赋值运算符,因此您问题中的代码就是利用它。 To see what's happening, add copy-assignment and move-assignment operators. 要查看正在发生的情况,请添加复制分配和移动分配运算符。

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;
}

With these in place, I get the expected output: 有了这些,我得到了预期的输出:

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!

As an aside, you're leaking memory. 顺便说一句,您正在泄漏内存。 Your destructor should look like this: 您的析构函数应如下所示:

~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)

You could also implement the assignment operator using the copy/swap idiom (below). 您还可以使用复制/交换惯用语(如下)实现赋值运算符。 This will add an extra constructor/destructor output due to the temporary, but it's good practice overall. 由于临时原因,这将添加一个额外的构造函数/析构函数输出,但这是一个好的做法。

Word& operator = (const Word& w) {
    Word temp(w);
    std::swap(ptr, temp.ptr);
    cout << "assignment operator: " << ptr << endl;
    return *this;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM