考虑以下代码:
#include <iostream>
using namespace std;
void Func(int&& i) {
++i;
}
int main() {
int num = 1234;
cout << "Before: " << num << endl;
Func(std::move(num));
cout << "After: " << num << endl;
}
Run Code Online (Sandbox Code Playgroud)
它的输出是:
Before: 1234
After: 1235
Run Code Online (Sandbox Code Playgroud)
显然,i正在内部进行修改Func,因为它i在被"转换"为r值引用之后被绑定到参数std::move.
好吧,我的观点:
移动对象意味着将资源的所有权从一个对象转移到另一个对象.但是,内置类型不包含资源,因为它们本身就是资源.转移他们持有的资源毫无意义.如示例所示,num修改了s值.它的资源,它的自我,是被修改的资源.
内置类型有移动语义吗?
另外,内置类型对象在移动后(如果是)是一个明确定义的行为吗?
move对象之后,新旧对象之间是否存在依赖关系?int main () {
int a = 100;
std::cout<<&a<<std::endl;
auto a_copy = a; // deduced as int
std::cout<<&a_copy<<std::endl;
auto a_move = std::move(a); // deduced as int
std::cout<<&a_move<<std::endl;
};
Run Code Online (Sandbox Code Playgroud)
输出:
0x7fffffffe094
0x7fffffffe098
0x7fffffffe09c
Run Code Online (Sandbox Code Playgroud) 如何让这个简单的类移动?我认为是正确的只会产生一堵错误之墙......
#include <iostream>
#include <sstream>
#include <utility>
class message
{
public:
message() = default;
// Move constructor
message( message &&other ) :
stream_( std::move( other.stream_ ) ) // Nope
{}
// Move assignment
message &operator=( message &&other )
{
if ( this != &other )
{
stream_ = std::move( other.stream_ ); // Nope #2
}
return *this;
}
private:
message( const message & ) = delete;
message &operator=( const message & ) = delete;
std::stringstream stream_;
// Other member variables …Run Code Online (Sandbox Code Playgroud) 我一直在试图解决C++ 11中的移动语义应该如何工作,而且我很难理解移动对象需要满足的条件.看看这里的答案并没有真正解决我的问题,因为无法看到如何以合理的方式将它应用于pimpl对象,尽管移动语义的参数非常适合pimpls.
我的问题最简单的说明涉及pimpl习语,如下所示:
class Foo {
std::unique_ptr<FooImpl> impl_;
public:
// Inlining FooImpl's constructors for brevity's sake; otherwise it
// defeats the point.
Foo() : impl_(new FooImpl()) {}
Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}
Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}
Foo & operator=(Foo rhs)
{
std::swap(impl_, rhs.impl_);
return *this;
}
void do_stuff ()
{
impl_->do_stuff;
}
};
Run Code Online (Sandbox Code Playgroud)
现在,一旦我离开了,我该怎么办Foo?我可以安全地销毁移动的物体,我可以分配给它,这两者都绝对是至关重要的.但是,如果我尝试do_stuff使用我的Foo,它会爆炸.在我为我的定义添加移动语义之前Foo,每个人都Foo满足了它的不变性do_stuff,而现在已不再是这样了.目前似乎并没有被大量的替代品,或者说,因为(例如)将被移至距离Foo …
我正在探索使用boost :: asio以及C++ 11功能.特别是,我专注于一个名为"async_tcp_echo_server.cpp"的示例,位于此处(代码也显示在我的问题的末尾):
我的问题涉及到tcp::socket成员socket_的的server类.在类的do_accept()方法中server,socket_传递给async_accept().(根据asio文档,async_accept()要求,作为其第一个参数,socket接受连接进入.)到目前为止,这么好.
下一个参数,即异步接受操作的回调,是一个lambda函数.lambda的主体构造一个新session对象,其构造函数也需要相同的对象socket.有趣的是,socket对象无法复制; 所以在这个例子中,使用了传递对象socket_成员的server对象std::move().
我知道"唯一的" socket_对象(它是对象的"永久"成员server)被"移动"到session对象中.精细 - socket物体不会被复制,而是被移动 - 每个人都很开心.
但是下次打电话会发生什么async_accept()?是否相同socket_(成员server),之前被移动,再次传入?当我们"移动"一个成员时,留下了什么?有无限socket物体的神奇喷泉吗?
或者这里发生的事情真的不那么明显了?当socket移入时session,"遗留/移动"对象(socket_成员server)的内容是否与"新" 对象自己的"尚未构建" 成员的内容交换?我甚至有意义吗?sessionsocket_
代码如下.程序流程相当简单.main()构造一个server对象.该 …
在C++编程语言第4版中,有一个向量实现的示例,请参阅消息末尾的相关代码.
uninitialized_move()通过从旧内存区域移动新T对象将其初始化到新内存区域.然后它调用原始T对象上的析构函数,即移动对象.为什么在这种情况下需要析构函数调用?
这是我的不完全理解:移动对象意味着移动对象拥有的资源的所有权被转移到移动对象.移动对象中的剩余部分是一些不需要销毁的内置类型的可能成员,当vector_base b超出范围时(在reserve ()调用之后,它们将被释放)).移动对象中的所有指针都将被置于nullptr或者使用某种机制来删除这些资源上移动对象的所有权以便我们安全,那么为什么在"vector_base b"时调用耗尽对象上的析构函数"在交换完成后,析构函数仍会解除内存的释放?
我理解在必须调用析构函数时需要显式调用析构函数,因为我们有一些东西需要破坏(例如drop元素)但是在vector_base的std :: move + deallocation之后我看不到它的含义.我在网上阅读了一些文本,我看到被移动对象的析构函数调用作为对象生命周期结束的信号(对谁或什么?).
请告诉我,析构函数还有哪些有意义的工作要做?谢谢!
下面的代码片段来自http://www.stroustrup.com/4th_printing3.html
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return; // never decrease allocation
vector_base<T,A> b {vb.alloc,size(),newalloc-size()}; // get new space
uninitialized_move(vb.elem,vb.elem+size(),b.elem); // move elements
swap(vb,b); // install new base
} // implicitly release old space
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
using T = Value_type<Out>; // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_) …Run Code Online (Sandbox Code Playgroud) c++ memory-management move-semantics c++11 explicit-destructor-call
在C++ 11中,引入了"移动语义",通过两个特殊成员实现:移动构造函数和移动赋值.这两个操作都保留了构造的移动对象.
将源保持在破坏状态不是更好吗?对于移动对象,你唯一能做的就是破坏它吗?
根据N3485§23.3.2.2:
(...)数组的隐式移动构造函数和移动赋值运算符要求T 分别为MoveConstructible或MoveAssignable.
因此,std::array如果元素的类型支持,则支持移动语义.大!
但是,这究竟意味着什么?我倾向于将这种类型描述为提供符合STL标准的接口的更安全的数组版本,但是,如果这是真的,那么std::array移动构造它的元素怎么样?我可以用普通数组做同样的事情吗?
如本回答所述,复制和交换习惯用法如下实现:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
Run Code Online (Sandbox Code Playgroud)
通过将MyClass的值作为operator =的参数,可以通过复制构造函数或移动构造函数构造参数.然后,您可以安全地从参数中提取数据.这可以防止代码重复并有助于异常安全.
答案提到您可以在临时中交换或移动变量.它主要讨论交换.但是,交换(如果未由编译器优化)涉及三个移动操作,而在更复杂的情况下,还需要额外的额外工作.当你想要的时候,就是将临时文件移动到assign-to对象中.
考虑这个更复杂的例子,涉及观察者模式.在这个例子中,我手动编写了赋值运算符代码.重点是移动构造函数,赋值运算符和交换方法:
class MyClass : Observable::IObserver …Run Code Online (Sandbox Code Playgroud) C++ 11"移动"是一个很好的功能,但我发现当与"复制"同时使用时,很难避免代码重复(我们都讨厌这个).下面的代码是我实现的一个简单的循环队列(不完整),两个push()方法几乎相同,除了一行.
我遇到过很多像这样的情况.任何想法如何避免这种代码重复而不使用宏?
===编辑===
在这个特定的例子中,重复的代码可以被重构并放入一个单独的函数中,但有时这种重构是不可用的或者不能轻易实现.
#include <cstdlib>
#include <utility>
template<typename T>
class CircularQueue {
public:
CircularQueue(long size = 32) : size{size} {
buffer = std::malloc(sizeof(T) * size);
}
~CircularQueue();
bool full() const {
return counter.in - counter.out >= size;
}
bool empty() const {
return counter.in == counter.out;
}
void push(T&& data) {
if (full()) {
throw Invalid{};
}
long offset = counter.in % size;
new (buffer + offset) T{std::forward<T>(data)};
++counter.in;
}
void push(const T& data) {
if (full()) { …Run Code Online (Sandbox Code Playgroud) c++11 ×10
move-semantics ×10
c++ ×9
boost ×1
boost-asio ×1
g++ ×1
invariants ×1
move ×1
pimpl-idiom ×1
stringstream ×1