C++ 移动赋值运算符和“如果”(此!=&;rhs)`
在类的赋值操作符中,通常需要检查被赋值的对象是否是调用对象,这样就不会把事情搞砸:C++ 移动赋值运算符和“如果”(此!=&;rhs)`,c++,c++11,move-semantics,move-assignment-operator,C++,C++11,Move Semantics,Move Assignment Operator,在类的赋值操作符中,通常需要检查被赋值的对象是否是调用对象,这样就不会把事情搞砸: Class& Class::operator=(const Class& rhs) { if (this != &rhs) { // do the assignment } return *this; } 移动分配操作符是否需要相同的内容?是否曾经出现过这样的情况:this==&rhs ? Class::operator=(Class&&
Class& Class::operator=(const Class& rhs) {
if (this != &rhs) {
// do the assignment
}
return *this;
}
移动分配操作符是否需要相同的内容?是否曾经出现过这样的情况:this==&rhs
? Class::operator=(Class&& rhs) {
?
}
在编写当前的
运算符=
函数时,由于您已经创建了右值引用参数const
,因此无法“窃取”指针并更改传入右值引用的值。。。你根本无法改变它,你只能从中阅读。我只会看到一个问题,如果你开始调用delete
指针等。在这个对象中,就像在普通的lvaue引用operator=
方法中一样,但是这种方法会破坏右值版本的观点。。。i、 例如,使用右值版本来执行通常留给const
-lvalue操作符=
方法的相同操作似乎是多余的
现在,如果您将运算符=
定义为接受一个非常量
右值引用,那么我看到需要进行检查的唯一方法是将此
对象传递给一个有意返回右值引用而不是临时值的函数
例如,假设有人试图编写一个运算符+
函数,并利用右值引用和左值引用的混合,以“防止”在对对象类型执行某些堆叠加法操作期间创建额外的临时值:
struct A; //defines operator=(A&& rhs) where it will "steal" the pointers
//of rhs and set the original pointers of rhs to NULL
A&& operator+(A& rhs, A&& lhs)
{
//...code
return std::move(rhs);
}
A&& operator+(A&& rhs, A&&lhs)
{
//...code
return std::move(rhs);
}
int main()
{
A a;
a = (a + A()) + A(); //calls operator=(A&&) with reference bound to a
//...rest of code
}
现在,根据我对右值引用的理解,不鼓励执行上述操作(即,您应该只返回一个临时的,而不是右值引用),但是,如果有人仍然这样做,然后,您需要检查以确保传入的右值引用没有引用与此
指针相同的对象。在编写当前的运算符=
函数时,由于您已经创建了右值引用参数const
,因此无法“窃取”指针并更改传入右值引用的值。。。你根本无法改变它,你只能从中阅读。我只会看到一个问题,如果你开始调用delete
指针等。在这个对象中,就像在普通的lvaue引用operator=
方法中一样,但是这种方法会破坏右值版本的观点。。。i、 例如,使用右值版本来执行通常留给const
-lvalue操作符=
方法的相同操作似乎是多余的
现在,如果您将运算符=
定义为接受一个非常量
右值引用,那么我看到需要进行检查的唯一方法是将此
对象传递给一个有意返回右值引用而不是临时值的函数
例如,假设有人试图编写一个运算符+
函数,并利用右值引用和左值引用的混合,以“防止”在对对象类型执行某些堆叠加法操作期间创建额外的临时值:
struct A; //defines operator=(A&& rhs) where it will "steal" the pointers
//of rhs and set the original pointers of rhs to NULL
A&& operator+(A& rhs, A&& lhs)
{
//...code
return std::move(rhs);
}
A&& operator+(A&& rhs, A&&lhs)
{
//...code
return std::move(rhs);
}
int main()
{
A a;
a = (a + A()) + A(); //calls operator=(A&&) with reference bound to a
//...rest of code
}
现在,根据我对右值引用的理解,不鼓励执行上述操作(即,您应该只返回一个临时的,而不是右值引用),但是,如果有人仍然这样做,那么您需要检查以确保传入的右值引用没有引用与此
指针相同的对象。哇,这里有太多的东西要清理
首先,复制并不总是实现复制分配的正确方法。几乎可以肯定的是,在哑_阵列
的情况下,这是一个次优解决方案
is fordumb_array
是将最昂贵的操作和最完整的功能放在底层的经典示例。它是完美的客户谁想要最完整的功能,并愿意支付性能罚款。他们得到的正是他们想要的
但是,对于那些不需要最全面的功能,而是寻求最高性能的客户来说,这是灾难性的。对他们来说,dumb_array
只是另一个他们必须重写的软件,因为它太慢了。如果dumb_array
的设计不同,它可以满足两个客户的需求,而不会对任何一个客户造成任何损害
满足这两个客户机的关键是在最低级别上构建最快的操作,然后在此基础上添加API,以更高的成本实现更全面的功能。也就是说,你需要强有力的例外担保,好吧,你自己付钱。你不需要它?这里有一个更快的解决方案
下面是dumb_数组
的快速基本异常保证拷贝分配操作符:
dumb_array& operator=(const dumb_array& other)
{
if (this != &other)
{
if (mSize != other.mSize)
{
delete [] mArray;
mArray = nullptr;
mArray = other.mSize ? new int[other.mSize] : nullptr;
mSize = other.mSize;
}
std::copy(other.mArray, other.mArray + mSize, mArray);
}
return *this;
}
dumb_array& operator=(dumb_array&& other)
{
assert(this != &other);
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
说明:
在现代硬件上,你可以做的一件更昂贵的事情就是到堆里去一趟。你能做的任何事情都是花时间和精力来避免陷入困境的。dumb_array
的客户端可能希望经常分配相同大小的数组。当他们这样做时,您只需要做一个memcpy
(隐藏在std::copy
下)。您不希望分配相同大小的新数组,然后取消分配相同大小的旧数组
现在,对于真正需要强异常安全性的客户:
template <class C>
C&
strong_assign(C& lhs, C rhs)
{
swap(lhs, rhs);
return lhs;
}
这其实是一个有争议的问题。有些人会说是的,绝对的,有些人会说不
我个人的意见是不,你不需要这张支票
理由:
当对象绑定到右值引用时,它是以下两种情况之一:
临时的
来电者希望您相信的对象是临时对象
如果对某个对象的引用是实际的临时对象,那么根据定义,对该对象的引用是唯一的。它不可能被整个p中的任何其他地方引用
Class&
Class::operator=(Class&& other)
{
assert(this != &other);
// ...
return *this;
}
dumb_array& operator=(dumb_array&& other)
{
assert(this != &other);
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
dumb_array& operator=(dumb_array&& other)
{
assert(this != &other || mSize == 0);
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
x = y;
x = std::move(y);
template <class T>
void
swap(T& x, T& y)
{
// assume &x == &y
T tmp(std::move(x));
// x and y now have a valid but unspecified state
x = std::move(y);
// x and y still have a valid but unspecified state
y = std::move(tmp);
// x and y have the value of tmp, which is the value they had on entry
}
dumb_array& operator=(dumb_array&& other)
{
delete [] mArray;
// set *this to a valid state before continuing
mSize = 0;
mArray = nullptr;
// *this is now in a valid state, continue with move assignment
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
dumb_array& operator=(dumb_array&& other)
{
if (this != &other)
{
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
}
return *this;
}
dumb_array& operator=(dumb_array&& other)
{
swap(other);
return *this;
}
Class& operator=(Class&&) = default;
Expression Return type Return value Post-condition
t = rv T& t t is equivalent to the value of rv before the assignment
Class &Class::operator=( Class &&rhs ) {
//...
return *this;
}
dumb_array& dumb_array::operator=(const dumb_array& other)
{
if (mSize != other.mSize)
{
delete [] mArray;
mArray = nullptr; // clear this...
mSize = 0u; // ...and this in case the next line throws
mArray = other.mSize ? new int[other.mSize] : nullptr;
mSize = other.mSize;
}
std::copy(other.mArray, other.mArray + mSize, mArray);
return *this;
}
class dumb_array
{
//...
void swap(dumb_array& other) noexcept
{
// Just in case we add UDT members later
using std::swap;
// both members are built-in types -> never throw
swap( this->mArray, other.mArray );
swap( this->mSize, other.mSize );
}
dumb_array& operator=(dumb_array&& other) noexcept
{
this->swap( other );
return *this;
}
//...
};
void swap( dumb_array &l, dumb_array &r ) noexcept { l.swap( r ); }
class dumb_array
{
//...
dumb_array& operator=(dumb_array&& other) noexcept
{
delete [] this->mArray; // kill old resources
this->mArray = other.mArray;
this->mSize = other.mSize;
other.mArray = nullptr; // reset source
other.mSize = 0u;
return *this;
}
//...
};
class dumb_array
{
//...
dumb_array& operator=(dumb_array&& other) noexcept
{
dumb_array temp{ std::move(other) };
this->swap( temp );
return *this;
}
//...
};
unique_ptr& operator=(unique_ptr&& x) {
delete ptr_;
ptr_ = x.ptr_;
x.ptr_ = nullptr;
return *this;
}
src.erase(
std::partition_copy(src.begin(), src.end(),
src.begin(),
std::back_inserter(even),
[](int num) { return num % 2; }
).first,
src.end());