【问题标题】:Move constructor not getting called, instead copy constructor gets called移动构造函数没有被调用,而是复制构造函数被调用
【发布时间】:2016-07-12 19:01:41
【问题描述】:

我正在尝试调用移动构造函数,但正在调用复制构造函数。 我做错了什么?

#include <iostream>
#include <string.h>

class X
{
    char* name;
public:
    X(const char* p)
    {
        name = new char[strlen(p) + 1];
        strcpy(name, p);
        std::cout<<"Constructor: "<<name<<"; Address: "<<this<<std::endl;
    }

    ~X()
    {
        std::cout<<"Destructor: "<<name<<"; Address: "<<this<<std::endl;
        delete [] name;
    }

    X(const X& a)
    {
        name = new char[strlen(a.name) + 1];
        strcpy(name, a.name);
        std::cout<<"Copy Constructor: "<<name<<"; Address: "<<this<<"; made from: "<<&a<<std::endl;
    }

    X& operator=(const X& a)
    {
        if (name)
        {
            delete [] name;
        }

        name = new char[strlen(a.name) + 1];
        strcpy(name, a.name);
        std::cout<<"Assignment operator: "<<name<<"; Address: "<<this<<"; made from: "<<&a<<std::endl;

        return *this;
    }

    X operator+(const X& a)
    {
        std::string s1 = name;
        std::string s2 = a.name;
        s1.append(" ");
        s1.append(s2);

        X x(s1.c_str());

        std::cout<<"operator+: "<<s1.c_str()<<"; Address: "<<&x<<"; Created from"
            <<this<<" and "<<&a<<std::endl;

        return x;
    }

    // move copy constructor
    X(X&& a)
    {
        name = a.name;
        a.name = nullptr;
        std::cout<<"Move Copy Constructor: "<<name<<"; Address: "<<this<<std::endl;
    }

    friend X fun(const X& a);
};

X fun(const X& a)
{
    std::cout<<"Inside fun()"<<std::endl;
    X p = a;
    return p;
}

int main()
{
    X h("Harry");
    X r("Ron");

    std::cout<<"Trying to create a temporary object"<<std::endl;
    X a = fun(h + r);
    std::cout<<"Check above if a temporay object was created"<<std::endl;

    return 0;
}

上述程序的O/P:

Constructor: Harry; Address: 0x79315dbc31b0
Constructor: Ron; Address: 0x79315dbc31c0
Trying to create a temporary object
Constructor: Harry Ron; Address: 0x79315dbc31e0
operator+: Harry Ron; Address: 0x79315dbc31e0; Created from0x79315dbc31b0 and 0x79315dbc31c0
Inside fun()
Copy Constructor: Harry Ron; Address: 0x79315dbc31d0; made from: 0x79315dbc31e0
Destructor: Harry Ron; Address: 0x79315dbc31e0
Check above if a temporay object was created
Destructor: Harry Ron; Address: 0x79315dbc31d0
Destructor: Ron; Address: 0x79315dbc31c0
Destructor: Harry; Address: 0x79315dbc31b0

这是否意味着程序无法创建临时对象? 我以为地址为 0x79315dbc31e0 的对象是临时对象。

注意:由于我的 gcc 很旧,我在支持 C++11 和 C++14 的在线 C++ 编译器之一上尝试了此代码。

【问题讨论】:

  • h + r的返回值是一个临时对象。输出有什么意外?你认为你在哪里“试图调用移动构造函数”?
  • 我期待“X a = fun(h + r);”调用移动复制构造函数。
  • 在该行中,afun 的返回值中移出,但这也是一个复制省略上下文。 X p 内部的 fun 被标识为 X a = fun(h+r);。如果您在禁用复制省略的情况下进行编译,您应该会看到移动构造函数调用。
  • @Saket a 指定一个临时对象,但a 是一个左值(所有命名变量都是左值),所以X&amp;&amp; 不会绑定到它。 (右值引用不能绑定到左值)。此外,您不能通过编写p = std::move(a); 来解决此问题,因为a 被声明为const。如果要移出临时对象,则不能将 const 引用绑定到临时对象。您将不得不改用X fun(X a);(可以使用左值和右值调用fun)或X fun(X&amp;&amp; a);(只能使用右值调用fun)。
  • 您还需要将 X p = a; 更改为 X p = std::move(a); 。我们正在考虑的复制省略位于 return p; 中。最初你说你认为X a = fun(h+r) 是移动操作,但现在你说你认为它是X p = a;

标签: c++ c++11 move-semantics move-constructor


【解决方案1】:

在“Inside fun()”之后对“复制构造函数”的调用实际上是在fun() 中的以下行中对复制构造函数的调用:

X p = a;

这是对复制构造函数的调用。您显然希望调用移动构造函数,因为:

X a = fun(h + r);

这个临时的被完全“忽略”掉了。整个序列:

X p = a;
return p;

编译器实际上将 p 构造为函数的返回值,因此“return p”什么也不做。

然后,因为函数的返回值用于在 main() 中实例化 a 对象,所以调用者本质上将 main() 的“a”传递给 fun(),作为要构造的对象fun().

您可以自己查看这些事件的顺序,只需使用调试器单步执行您的代码。

【讨论】:

  • 我认为X p = a; 应该调用移动构造函数。 a这里不是临时的吗?
  • 不,'a' 不是临时的。 'a' 是此函数的完全限定参数。 'h+r' 在其表达式的上下文中是一个临时的。但是,在函数内部,“a”是一个离散对象。
  • @SamVarshavchik 什么是“离散对象”? a 指定一个临时对象。
猜你喜欢
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
相关资源
最近更新 更多