2015-10-24 35 views
6

我有一个关于使用unique-ptrs的问题。我得到this answer建议使用移动对象。我定义如下的一类:在C++ 11中定义移动对象是否有意义?

class B { 
    const string objName; 
public: 

    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 

    B(const string & name) : 
      objName(name) { 

    } 

    virtual ~B(); 

    const string name() const { return objName;}; 
} 

,我这个线叫B:

class A { 
A(){} 
void take(B b); 
} 

A a; 
B b("test"); 
cout<<b.name(); 
a.take(std::move(b)); 
cout<<b.name(); 

我的问题:

  1. 即使我已经拖欠了移动构造函数,我不能写a.take(b),我正在编译错误。 (b)
  2. 在结果中,“测试”结果显示“复制构造函数”被删除,但似乎合乎逻辑的选择是使用移动构造函数,而不需要写入std :: move打印两次。为什么在调用move之后b对象没有被移除?如果b对象仍然存在并且它的一个副本已经发送给a.take(move(b)),那么这意味着我们没有任何用于右值对象的移动。
  3. 是否使用上述移动对象(删除复制构造函数和赋值运算符以及默认移动构造函数和移动赋值)是否是一种很好的做法?
+0

尝试用更长的字符串替换“test”,看看会发生什么。一般而言,移动对象的值是不确定的;它可能是旧的价值或不是。 –

+3

如果你想从它移动,你需要从'objName'中移除'const'。 –

+0

@AlanStokes删除const会导致测试将被打印一次。这是否意味着永恒的记忆永远不会被移动?或者他们会被复制? – Govan

回答

4

是的,有一点。管理资源(可能是物理资源)的对象不能/不应该在对象之间共享,这是想到的第一个例子。

1)你写错了。基于这个问题以及您之前的问题,我认为您需要这些。

class B { 
    string objName; 
public: 
    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 
    B(const std::string & name) : 
      objName(name) {} 
    virtual ~B() {} 
    std::string name() const { return objName;} 
}; 

class A { 
public: 
    std::vector<B> v; 
    void take(B && b) 
    { 
     v.push_back(std::move(b)); 
    } 
}; 

int main() 
{ 
    A a; 
    B b("test"); 

    std::cout << "Before: " << b.name() << std::endl; 
    a.take(std::move(b)); 
    std::cout << "After: " << b.name() << std::endl; 

    std::cout << "A has elements: " << std::endl; 
    for(B &b : a.v) 
     std::cout << " " << b.name() << std::endl; 
} 

2)您正在访问已移动的值,这没有任何意义! This answer已经做了很好的解释,但下面我还包含了std :: move的STL参考文本。

http://en.cppreference.com/w/cpp/utility/move

除另有规定外,有 从被放置在一个有效的,但不确定状态被移动的所有标准库对象。即, 只有没有先决条件的功能(例如运算符 )才能在对象移出后安全地使用。

3)我发现在我的经验中有两个合法用途。在这两种情况下,只能移动的对象都控制着一个物理资源,如果两个对象共享这个资源就会破坏一切。

+2

请注意,不需要明确删除副本和赋值运算符。用户定义的移动语法禁止复制,请参阅现场示例[这里](http://coliru.stacked-crooked.com/a/d20de6c4f219a22e)。 – vsoftco

+0

@tweej为什么我应该使用move-only objet而不是unique-ptr? – Govan

+0

@Govan:实际上,对于一个不能共享的资源,'unique_ptr'是一个建筑物砖,而你的移动物体将它包起来呈现一个愉快的界面。如果它不能共享,你甚至会写一个拷贝构造函数? –

2

约3:当然有。无论如何,我可以考虑很多可以移动但不能被复制的对象的例子。

一个例子是一个Socket类:
1)你想给一个默认的构造函数为“空”的Socket仍然没有收到任何IP或端口。
2)你想给一个构造函数来获得一个IP和端口。基于结构的Socket会尝试连接,如果连接失败
3)明显的一点就是析构函数可能抛出异常 - 它会断开该对象并释放该对象可能持有

任何潜在的系统资源是什么关于移动构造函数与复制构造函数?
比方说,我有一个功能,创建一个套接字并返回它。如果我调用意味着:
的复制构造函数,则新复制的套接字(返回值)将尝试连接到源套接字所连接的相同IP和端口。这可能根本不可能。
源套接字将被断开连接并被破坏

试图复制套接字很可能会造成巨大的混乱。 相反,移动构造函数很好地解决了这个问题:
返回值接收所有底层操作系统资源而不断开连接或销毁它们。
源套接字保持空白,并且析构函数没有任何内容可以断开连接或销毁。

看起来套接字最可能只是被移动,而不是被复制。

这可能适用于“Process”类 - 如果我尝试通过复制返回一个进程,我可能会尝试再次打开相同的进程,只关闭原来的进程。一团糟!通过移动周围的过程我不这样做。我只是将这个过程从功能转移到功能上。

+0

感谢您的回答。我了解移动和复制之间的差异和差异。我想知道的是需要移动右值对象。您可以通过unique_ptr实现套接字。问题是move(unique_ptr (new Socket()))或move(Socket())之间的差异 – Govan