提问人:Govan 提问时间:10/24/2015 最后编辑:CommunityGovan 更新时间:8/24/2018 访问量:10158
在 c++11 中定义仅移动对象有意义吗?
Is there a point to define move-only objects in c++11?
问:
我之前有一个关于使用 unique-ptrs 的问题。我得到这个答案,建议使用仅移动对象。我定义了一个类,如下所示:
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();
我的问题:
- 即使我默认了移动构造函数,我也无法编写 a.take(b) 并且出现编译错误。我知道复制结构被删除了,但似乎合乎逻辑的选择是在默认时使用 move 构造函数,而无需像这样编写 std::move a.take(b)
- 在结果中,“测试”被打印两次。为什么调用 move 后 b 对象没有被移除?如果 b 对象仍然存在,并且它的副本已发送到 a.take(move(b)),则意味着我们对右值对象没有任何使用 move。
- 使用上述仅移动对象(删除复制构造函数和赋值运算符以及默认的移动构造函数和移动赋值)是否是一种好的做法?
答:
是的,有一点。管理资源(也许是物理资源)的对象不能/不应该在对象之间共享,这是我想到的第一个例子。
1)你写错了。这是我认为你根据这个问题和你之前的问题想要的。
class B {
std::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) 您正在访问一个已从中移动的值,这没有任何意义!这个答案已经很好地解释了,但下面我还包含了 std::move 的 STL 参考文本。
http://en.cppreference.com/w/cpp/utility/move
除非另有指定,否则所有具有 已从中移动,处于有效但未指定的状态。那是 只有没有前提条件的函数,例如赋值 运算符,可以在对象移动后安全地用于对象。
3)根据我的经验,我发现了两个合法的用途。在这两种情况下,仅移动对象都控制着一个物理资源,如果由两个对象共享,则会破坏所有资源。
评论
unique_ptr
关于3:当然有。我可以想到许多可以移动但不能复制的对象的例子,或者无论如何都不应该复制。
一个例子是 Socket 类:
1) 你想为“空”Socket 提供一个默认构造函数,它仍然没有收到任何 IP 或端口。
2)你想要一个获取IP和端口的构造函数。在构造时,Socket 将尝试连接,如果连接失败
,可能会抛出异常 3) 显而易见的是析构函数 - 它将断开对象的连接并释放该对象可能持有的任何底层系统资源
移动构造函数与复制构造函数怎么样?
假设我有一个函数创建一个套接字并返回它。如果我调用复制构造函数,这意味着:
新复制的套接字(返回值)将尝试连接到源套接字连接到的同一 IP 和端口。这可能根本不可能。
源套接字将被断开并销毁
尝试复制套接字很可能会造成巨大的混乱。
相反,移动构造函数很好地解决了这个问题:
返回值接收所有底层操作系统资源,而不会断开它们或破坏它们。
源套接字保持为空,析构函数没有任何东西可以断开连接或销毁。
套接字似乎很可能只会被移动,而不是被复制。
这可能适用于“Process”类 - 如果我尝试通过复制返回一个进程,我可能会尝试再次打开相同的进程,只是关闭了原始进程。一团糟!通过移动过程,我不这样做。我只是将过程从一个功能移动到另一个功能。
评论
const
objName
std::unique_ptr