隐式移动构造函数和赋值运算符
Implicit move constructor and assignment operator
隐式移动构造函数进行成员移动和隐式移动赋值运算符进行成员赋值是什么意思?
来自https://en.cppreference.com/w/cpp/language/move_constructor:
For non-union class types (class and struct), the move constructor
performs full member-wise move of the object's bases and non-static
members, in their initialization order, using direct initialization
with an xvalue argument. If this satisfies the requirements of a
constexpr constructor, the generated move constructor is constexpr.
来自https://en.cppreference.com/w/cpp/language/move_assignment:
For non-union class types (class and struct), the move assignment
operator performs full member-wise move assignment of the object's
direct bases and immediate non-static members, in their declaration
order, using built-in assignment for the scalars, memberwise
move-assignment for arrays, and move assignment operator for class
types (called non-virtually).
对于以下示例性 class 模板,隐式成员是否会像这样:
template<class T>
class Holder {
public:
Holder(int size) : m_size(size) { m_data = new T[m_size]; }
Holder(Holder && other) :
m_size(std::move(other.m_size)),
m_data(std::move(other.m_data))
{}
Holder& operator=(Holder && other) {
if(this == &other) return *this;
m_data = std::move(other.m_data);
m_size = std::move(other.m_size);
return *this;
}
~Holder() { delete [] m_data; }
private:
T* m_data;
int m_size;
};
另外,上面例子中的std::move()
会把资源转移到什么地方?
如果您进一步查看链接页面,您会看到 classes 编译器生成的移动构造函数(和移动赋值运算符)实际上是 Trivial:
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
- it is not user-provided (meaning, it is implicitly-defined or defaulted);
- T has no virtual member functions;
- T has no virtual base classes
- the move constructor selected for every direct base of T is trivial;
- the move constructor selected for every non-static class type (or array of class type) member of T is trivial;
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the
object representation as if by std::memmove. All data types
compatible with the C language (POD types) are trivially movable.
(强调我的)
这两个成员变量是 POD 类型,因此可以轻松移动。由于您的 class 不是虚拟的,并且它不包含非平凡的成员,因此它是平凡的,所有数据成员都将被复制。如评论中所述,这将导致双重删除您的指针和 UB。
由于是这种情况,您需要通过获取移动对象指针的所有权并将其设置为 nullptr
来正确实现移动语义。或者更好的是,只需使用 std::vector
甚至 std::unique_ptr
.
隐式移动构造函数进行成员移动和隐式移动赋值运算符进行成员赋值是什么意思?
来自https://en.cppreference.com/w/cpp/language/move_constructor:
For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument. If this satisfies the requirements of a constexpr constructor, the generated move constructor is constexpr.
来自https://en.cppreference.com/w/cpp/language/move_assignment:
For non-union class types (class and struct), the move assignment operator performs full member-wise move assignment of the object's direct bases and immediate non-static members, in their declaration order, using built-in assignment for the scalars, memberwise move-assignment for arrays, and move assignment operator for class types (called non-virtually).
对于以下示例性 class 模板,隐式成员是否会像这样:
template<class T>
class Holder {
public:
Holder(int size) : m_size(size) { m_data = new T[m_size]; }
Holder(Holder && other) :
m_size(std::move(other.m_size)),
m_data(std::move(other.m_data))
{}
Holder& operator=(Holder && other) {
if(this == &other) return *this;
m_data = std::move(other.m_data);
m_size = std::move(other.m_size);
return *this;
}
~Holder() { delete [] m_data; }
private:
T* m_data;
int m_size;
};
另外,上面例子中的std::move()
会把资源转移到什么地方?
如果您进一步查看链接页面,您会看到 classes 编译器生成的移动构造函数(和移动赋值运算符)实际上是 Trivial:
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
- it is not user-provided (meaning, it is implicitly-defined or defaulted);
- T has no virtual member functions;
- T has no virtual base classes
- the move constructor selected for every direct base of T is trivial;
- the move constructor selected for every non-static class type (or array of class type) member of T is trivial;
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.
(强调我的)
这两个成员变量是 POD 类型,因此可以轻松移动。由于您的 class 不是虚拟的,并且它不包含非平凡的成员,因此它是平凡的,所有数据成员都将被复制。如评论中所述,这将导致双重删除您的指针和 UB。
由于是这种情况,您需要通过获取移动对象指针的所有权并将其设置为 nullptr
来正确实现移动语义。或者更好的是,只需使用 std::vector
甚至 std::unique_ptr
.