[英]How to define a move constructor?
I am trying some new C++11 features on visual studio 11, started with the move constructor.我正在 Visual Studio 11 上尝试一些新的 C++11 功能,从移动构造函数开始。 I wrote a simple class called "MyClass" containing a move constructor:
我编写了一个名为“MyClass”的简单类,其中包含一个移动构造函数:
class MyClass
{
public:
explicit MyClass( int aiCount )
: mpiSize( new int( aiCount ) ),
miSize2( aiCount)
{
}
MyClass( MyClass&& rcOther )
: mpiSize( rcOther.mpiSize )
, miSize2( *rcOther.mpiSize )
{
rcOther.mpiSize = 0;
rcOther.miSize2 = 0;
}
~MyClass()
{
delete mpiSize;
}
private:
int *mpiSize;
int miSize2;
};
I got there questions here:我在这里有问题:
MSVC++ implemented move constructors before the final version of the standard was out. MSVC++ 在标准的最终版本发布之前实现了移动构造函数。 In the version of the standard MSVC++'s implementation was based on, the rules for generating a default move constructor were ridiculously more strict than they are in the final version of the standard.
在基于 MSVC++ 实现的标准版本中,生成默认移动构造函数的规则比标准的最终版本严格得多。 See here: Why is this code trying to call the copy constructor?
请参阅此处: 为什么此代码试图调用复制构造函数? (specifically this answer and the comments on it) for more info on that.
(特别是这个答案及其评论)以获取更多信息。 This has not been and will not be fixed in Visual Studio 11,
for some unknown stupid reason
because they had other priorities.这在 Visual Studio 11 中没有也不会修复,
因为一些未知的愚蠢原因,
因为他们有其他优先事项。
No, you need to call std::move
on the members of rcOther
, and you initialise members with the corresponding members from the dying object (you misnamed miSize
):不,您需要对
rcOther
的成员调用std::move
,并使用垂死对象中的相应成员初始化成员(您错误命名为miSize
):
MyClass( MyClass&& rcOther ) : mpiSize( std::move(rcOther.mpiSize) ) , miSize2( std::move(rcOther.miSize2) ) { rcOther.mpiSize = 0; }
It doesn't make a difference for built in types like int
and int*
, but it definitely makes a difference for user-defined types.它对像
int
和int*
这样的内置类型没有影响,但对用户定义的类型肯定有影响。
std::move
just returns the argument casted into a T&&
, an rvalue-reference, so that the correct constructor (the move constructor, T(T&&)
) is called for each of the sub-objects.std::move
只返回转换为T&&
的参数,一个右值引用,以便为每个子对象调用正确的构造函数(移动构造函数, T(T&&)
)。 If you don't use std::move
on the members of the dying object, they will be treated like T&
, and the copy constructor of your subobjects ( T(T&)
) will be called instead of the move constructor.std::move
,它们将被视为T&
,并且您的子对象( T(T&)
)的复制构造函数将被调用而不是移动构造函数。 That is very bad and thwarts almost the entire purpose of you having written a move constructor.
3. You are doing some unnecessary things, like setting the integer to 0. You only need to set the pointer to 0 so that delete
ing it won't delete the resource of the new object you created. 3.你在做一些不必要的事情,比如将整数设置为0。你只需要将指针设置为0,这样
delete
ing它就不会删除你创建的新对象的资源。
Also, if this is not a didactic exercise, you may want to consider using std::unique_ptr
instead of managing the lifetime of your own object.此外,如果这不是教学练习,您可能需要考虑使用
std::unique_ptr
而不是管理您自己的对象的生命周期。 This way you don't even have to write a destructor for your class.这样你甚至不必为你的类编写析构函数。 Note that if you do that, using
std::move
to initialise the member from the dying member in the move constructor would be mandatory.请注意,如果您这样做,则必须使用
std::move
从移动构造函数中的垂死成员中初始化该成员。
The compiler does generate a move constructor if you don't do so – after a fashion.如果您不这样做,编译器确实会生成一个移动构造函数 - 在一种方式之后。 However, the compiler cannot second-guess your motives so it doesn't know what the pointer in your class does.
但是,编译器无法猜测您的动机,因此它不知道您的类中的指针是做什么的。 In particular, it doesn't know that the pointer confers ownership of memory and needs to be nulled out.
特别是,它不知道指针授予内存所有权,需要清空。
The move constructor is correct1 but the rest of the class isn't, you are violating the rule of three : your class needs an appropriate copy constructor and copy assignment operator. 移动构造函数是正确的
1但类的其余部分不是,您违反了三规则:您的类需要适当的复制构造函数和复制赋值运算符。
A better way to write the move constructor looks as follows:编写移动构造函数的更好方法如下所示:
MyClass(MyClass&& rcOther)
: mpiSize(std::move(rcOther.mpiSize))
, miSize2(std::move(rcOther.miSize2))
{
rcOther.mpiSize = 0;
}
Two comments:两条评论:
rcOther.mpiSize
?rcOther.mpiSize
? While this isn't wrong, it also makes no sense and is misleading. But an even better way is to rely on pre-existing facilities .但更好的方法是依靠预先存在的设施。 In this case, you want to model memory ownership.
在这种情况下,您希望对内存所有权进行建模。 A naked pointer does this poorly, you should use a
std::unique_ptr
instead.裸指针在这方面做得很差,您应该改用
std::unique_ptr
。 This way, you don't need to implement either destructor nor move constructor since the the auto-generated methods do the right thing.这样,您不需要实现析构函数或移动构造函数,因为自动生成的方法做正确的事情。
1 Caveat : See Seth's answer for a better explanation that mentions std::move
(which is a no-op in this particular case, however). 1警告:请参阅 Seth 的回答以获得更好的解释,其中提到了
std::move
(然而,在这种特殊情况下这是一个无操作)。
Since C++14 you can take advantage of the std::exchange()
convenience function template for defining the move constructor.从 C++14 开始,您可以利用
std::exchange()
便利函数模板来定义移动构造函数。 This may result in a more concise move constructor definition:这可能会导致更简洁的移动构造函数定义:
MyClass(MyClass&& other) noexcept:
mpiSize(std::exchange(other.mpiSize, nullptr)),
miSize2(std::exchange(other.miSize2, 0))
{}
std::exchange(other.mpiSize, nullptr)
replaces the value of other.mpiSize
with nullptr
but returns the value other.mpiSize
had prior to the replacement. std::exchange(other.mpiSize, nullptr)
取代的价值other.mpiSize
与nullptr
,但返回值other.mpiSize
之前更换了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.