理解用函数返回的对象移动语义

时间:2017-07-05 09:28:06

标签: c++ constructor move-semantics

我从learncpp.com上看到了以下程序,作为理解移动语义的一个例子。我试着运行这个程序,看看程序流程是如何工作的,如教程中所述。

但是,我在函数调用中无法理解它。

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

据说它将使用移动构造函数,但是当我设置断点并进行调试时,它根本就没有这样做。

任何人都可以帮助我理解为什么不在这里调用移动构造函数?编译器是否在这里?如果是这样,如何修改它以便它将调用移动构造函数。请解释这里发生的事情。

另外,如果您可以分享一些调用移动构造函数的示例以及如何使用移动构造函数代替复制构造函数,那将会很棒。

#include <iostream>

template<class T>
class Auto_ptr4
{
    T* m_ptr;
public:
    Auto_ptr4(T* ptr = nullptr)
    :m_ptr(ptr)
    {
    }

    ~Auto_ptr4()
    {
        delete m_ptr;
    }

    // Copy constructor
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4(const Auto_ptr4& a)
    {
        m_ptr = new T;
        *m_ptr = *a.m_ptr;
    }

    // Move constructor
    // Transfer ownership of a.m_mptr to m_ptr
    Auto_ptr4(Auto_ptr4&& a)
    : m_ptr(a.m_ptr)
    {
        a.m_ptr = nullptr;
    }

    // Copy assignment
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4& operator=(const Auto_ptr4& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;

        // Release any resource we're holding
        delete m_ptr;

        // Copy the resource
        m_ptr = new T;
        *m_ptr = *a.m_ptr;

        return *this;
    }

    // Move assignment
    // Transfer ownership of a.m_ptr to m_ptr
    Auto_ptr4& operator=(Auto_ptr4&& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;

        // Release any resource we're holding
        delete m_ptr;

        // Transfer ownership of a.m_ptr to m_ptr
        m_ptr = a.m_ptr;
        a.m_ptr = nullptr;

        return *this;
    }

    T& operator*() const { return *m_ptr; }
    T* operator->() const { return m_ptr; }
    bool isNull() const { return m_ptr == nullptr; }
};

class Resource
{
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

int main()
{
    Auto_ptr4<Resource> mainres;
    mainres = generateResource(); // this assignment will invoke the move assignment

    return 0;
}

由于

1 个答案:

答案 0 :(得分:4)

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

您的代码编写方式非常允许复制省略。 C ++标准有一整节专门讨论这个用例。

[class.copy.elision/1.1]

  

在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称时(函数参数或由处理程序的异常声明引入的变量除外)[[除外] .handle]))与函数返回类型相同的类型(忽略cv-qualification),通过将自动对象直接构造到函数调用的返回对象中,可以省略复制/移动操作

如果你想要防止elision,你需要返回一个不能合法省略的表达。幸运的是,在这种情况下,它也很容易强制调用移动构造函数:

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return std::move(res); // this return value will invoke the move constructor
}

由于现在表达式不是自动变量名,因此Auto_ptr4<Resource>&&Auto_ptr4<Resource>完全相同。该消息并未被删除。

这对于学习目的来说很好,但不能在实际代码中完成。复制省略是一个非常好的事情,使更高效的代码。让编译器尽可能为你做。