2

我怀疑我对移动语义一无所知。鉴于以下代码,我希望调试器(MSVC2010SP1)按以下顺序调用 Proxy 的成员:

  • Proxy(Resource*)建造临时的getProxy
  • Proxy(Proxy&& other)移动建设p
  • ~Proxy()破坏临时的空壳,它的胆量被移动占据了
  • ~Proxy() p超出范围

    class Resource
    {
        void open(){}
    public:
        void close(){}
        Proxy && getProxy();
    };
    class Proxy
    {
        Resource *pResource_;
        Proxy(const Proxy& other); //disabled
        Proxy& operator=(const Proxy& other); //disabled
    public:
        Proxy(Resource *pResource):pResource_(pResource){}
        Proxy(Proxy&& other):pResource_(other.pResource_){other.pResource_ = nullptr;}
        ~Proxy()
        {
            if(pResource_)
                pResource_->close();
            pResource_ = nullptr;
        }
    };
    
    Proxy && Resource::getProxy()
    {
            open();
            return Proxy(this);
    }
    
    //somewhere else, lets say in main()
    Resource r;
    {
        auto p = r.getProxy(); 
    }   // p goes out of scope
    

相反,顺序是:

  • Proxy(Proxy*)
  • ~Proxy() //这已经close()比预期的更早调用了
  • Proxy(Proxy&& other) //在销毁后移动给出p.pResource_一个值nullptr
  • ~Proxy() //p超出范围

这对我来说毫无意义。我想要做的是跟踪代理类的生命周期,通过移动构造函数将关闭资源的工作从一个对象传递到另一个对象。

4

2 回答 2

6

getProxy()返回对临时对象的引用,该引用在函数结束时超出范围并导致悬空引用。

于 2013-02-04T14:23:52.783 回答
2

通过右值引用返回实际上不会导致任何东西被移动。它只是通过引用返回。但是,它与返回左值引用不同,因为调用返回右值引用的函数的表达式是 xvalue(与左值相反)。然后可以从中移动 xvalue(作为右值表达式的子集)。如果您想从返回左值引用的函数的返回对象中移动,则必须使用std::move它使其成为右值。

你很少会想要真正返回一个右值引用。它唯一模糊的常见用途是允许移动对象的私有成员。如果您希望在从函数返回时移动对象,只需按值返回即可。在您的情况下,如果返回类型getProxy为 just Proxy,则临时对象将从返回的对象中移出,然后将其移入p(保存任何省略)。

正如您所拥有的,您的临时对象(由 构造Proxy(this))在语句结束时被销毁return- 这是析构函数的第一次调用。返回的引用现在引用了一个无效的对象,并且p是通过从这个无效的引用移动来构造的。这给了你未定义的行为。

于 2013-02-04T14:34:50.037 回答