C ++析构函数通过引用

时间:2018-04-11 02:18:27

标签: c++ move c++17 move-semantics

我想与您分享一个我无法解决的小问题,这是代码(仅供测试):

#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <type_traits>
#include <sstream>

struct Procedure {
    Procedure(HANDLE)
    { std::cout << "ctor w/connection: " << this << std::endl; }

    ~Procedure()
    { std::cout << "dtor: " << this << std::endl; }

    Procedure(Procedure &&rhs) {
        std::cout << "ctor w/move: " << this << std::endl;
        this->m_Params = std::move(rhs.m_Params);
    }

    Procedure& operator= (Procedure &&rhs) {
        std::cout << "operator= w/move: " << this << std::endl;
        if (this != &rhs) this->m_Params = std::move(rhs.m_Params);
        return *this;
    }

    Procedure& AppendParam(const std::string &str) {
        std::cout << "appendparam: " << this << std::endl;
        m_Params.push_back(str);
        return *this;
    }

    void Execute( const std::string &str) {
        std::stringstream ss;
        ss << str << '(';
        for (int i = 0, mx = m_Params.size(); i < mx; ++i) {
            ss << '\'' << m_Params[i] << '\'';
            if (i < mx - 1) ss << ',';
        }
        ss << ");";
        std::cout << "calling: " << this << " : " << ss.str() << std::endl;
    }


private:
    Procedure(const Procedure &) = delete;
    Procedure& operator=(const Procedure &) = delete;
    std::vector<std::string> m_Params;
};

Procedure ProcedureCaller()
{ return Procedure(nullptr); }


int __cdecl main() {
    std::cout << "test1---------------------" << std::endl; {
        auto &proc = ProcedureCaller().AppendParam("param_1").AppendParam("param_2");
        proc.Execute("sp_test");
    }

    std::cout << "test2--------------------" << std::endl; {
        auto proc = ProcedureCaller();
        proc.AppendParam("param_A").AppendParam("param_B");
        proc.Execute("sp_test_2");
    }

    std::cout << "test3--------------------" << std::endl; {
        ProcedureCaller().AppendParam("param_AA").AppendParam("param_BB").Execute("sp_test_2");
    }
    return 0;
}

以下是我得到的结果:

test1---------------------
ctor w/connection: 00F8FC98
appendparam: 00F8FC98
appendparam: 00F8FC98
dtor: 00F8FC98
calling: 00F8FC98 : sp_test();
test2--------------------
ctor w/connection: 00F8FD70
appendparam: 00F8FD70
appendparam: 00F8FD70
calling: 00F8FD70 : sp_test_2('param_A','param_B');
dtor: 00F8FD70
test3--------------------
ctor w/connection: 004FFB20
appendparam: 004FFB20
appendparam: 004FFB20
calling: 004FFB20 : sp_test_2('param_AA','param_BB');
dtor: 004FFB20

我有几个问题:
1-为什么&#34; test1&#34;在它的范围结束之前被调用?我的意思是,代码甚至没有调用Execute方法 2-如果&#34; test1&#34;是一个时间对象,为什么我没有看到move ctor的日志,或者至少是编译错误,因为它试图使用已删除的copy ctor
3-&#34; test1&#34;之间的区别是什么?和&#34; test2&#34;,我希望能够以我想要的任何方式拨打Execute
4-我错过了什么?
感谢。

2 个答案:

答案 0 :(得分:5)

这是一个更简单的版本,展示了同样的问题:

struct X {
    X() = default;
    ~X() { std::cout << "dtor\n"; }
    X& self() { return *this; }
};

int main()
{
    X& x = X().self();
    std::cout << "here?\n";
}

此程序在打印dtor之前打印here。为什么?问题是,我们有一个临时的(X()),它不会延长生命周期,所以它会在包含它的表达式的末尾被破坏(X().self())。当你得到它的引用时,它不是延长生命周期的魔法引用之一 - 你得到的只是对一个立即超出范围的对象的引用。

终身延期仅在非常有限的情况下发生。临时必须立即绑定到引用,这只能发生在const引用:

X const& x = X();
std::cout << "here\n";

现在,这会在here之前打印dtor

此外,没有传递寿命延长。即使在最初的例子中我们做了:

X const& x = X().self();

我们仍然会有一个悬垂的参考。

答案 1 :(得分:0)

  1. 在&#34; test1&#34;例如,proc引用的对象是临时的。它在创建它的完整表达式的末尾超出范围,proc立即悬空。请注意,没有生命周期扩展的原因有两个:a)生命周期扩展只发生const lvalue-references和rvalue-references,b)生命周期扩展只发生在prvalues上,而AppendParam返回的引用是一个左右。
  2. proc在&#34; test1&#34; case是引用,而不是对象。不会发生移动或复制,因为没有要移动或复制的对象。引用绑定到ProcedureCaller返回的临时对象,并且在下一个;时超出范围。
  3. &#34; test1&#34;之间的区别和&#34; test2&#34;是proc是&#34; test1&#34;中的引用和#34; test2&#34;中的实际对象。如果您试图在&#34; test2&#34;中使proc成为引用。编译器会抱怨的情况。只有const左值引用和右值引用可以绑定到prvalue(例如从函数返回的对象)。唯一的原因是它在&#34; test1&#34;案件是你已经洗过&#34; prvalue通过返回lvalue-reference的方法。