练习13.49:
StrVec类的移动构造函数和移动赋值运算符
//移动构造函数
StrVec::StrVec(StrVec&& s)noexcept :elements(s.elements), first_free(s.first_free), cap(s.cap)
{
//令移后源对象进入状态-----对其运行析构函数是安全的
s.elements = s.first_free = s.cap = nullptr;
}
//移动赋值运算符
StrVec& StrVec::operator=(StrVec&& rhs)noexcept
{
if (this != &rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
String类的移动构造函数和移动赋值运算符
//移动构造函数
String::String(String&& s)noexcept:elements(s.elements),first_free(s.first_free),cap(s.cap)
{
//令移后源对象进入状态-----对其运行析构函数是安全的
s.elements = s.first_free = s.cap = nullptr;
}
//移动赋值运算符
String& String::operator=(String&& rhs)noexcept
{
if (this != &rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
Message类的移动构造函数和移动赋值运算符
void Message::move_Folders(Message* m)
{
//使用set的移动赋值运算符
folders = std::move(m->folders);
//遍历每一个fFolder,从中删除旧的Message
//将本Message添加到Folder中
for (auto f : folders)
{
f->remMsg(m);
f->addMsg(this);
}
//确保销毁m是无害的
m->folders.clear();
}
//移动构造函数
Message::Message(Message&& m) :contents(std::move(m.contents))
{
move_Folders(&m);
}
//移动赋值运算符
Message& Message::operator=(Message&& rhs)
{
//检查自赋值情况
if (this != &rhs)
{
//将本Message从所有的Folders中删除
remove_from_Folders();
//移动赋值
contents = std::move(rhs.contents);
//更新Folders
move_Folders(&rhs);
}
return *this;
}
练习13.50:
向String类的移动构造函数和移动赋值运算符中添加打印语句
//移动构造函数
String::String(String&& s)noexcept:elements(s.elements),first_free(s.first_free),cap(s.cap)
{
//令移后源对象进入状态-----对其运行析构函数是安全的
s.elements = s.first_free = s.cap = nullptr;
cout << "------String move constructor------" << endl;
}
//移动赋值运算符
String& String::operator=(String&& rhs)noexcept
{
if (this != &rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
cout << "------String move assignment operator------" << endl;
return *this;
}
测试代码
int main()
{
//测试
String s1("hello ");
String s2("world!!!");
String s3("C++");
vector<String>vs;
//移动构造
String s4(std::move(s2));
String s5(std::move(s1));
//移动赋值运算符
s2 = std::move(s3);
system("pause");
return 0;
}
结果
练习13.51:
返回的是即将销毁的临时变量unique_ptr,是一个右值绑定的引用,所以这不是拷贝,而是移动,窃取资源而不是拷贝资源,所以是合法的
练习13.52:
在第一个赋值中,右侧运算对象是一个左值,因此移动构造函数是不可行的。rhs将使用拷贝构造函数来初始化。拷贝构造函数将分配一个新string,并拷贝 hp2指向的string
在第二个赋值中,我们调用std: : move 将一个右值引用绑定到hp2上。在此情况下,拷贝构造函数和移动构造函数都是可行的。但是,由于实参是一个右值引用,移动构造函数是精确匹配的。移动构造函数从hp2拷贝指针,而不会分配任何内存
不管使用的是拷贝构造函数还是移动构造函数,赋值运算符的函数体都swap两个运算对象的状态。交换HasPtr 会交换两个对象的指针(及int)成员。在swap 之后,rhs中的指针将指向原来左侧运算对象所拥有的 string。当rhs离开其作用域时,这个string将被销毁
练习13.53:
赋值运算符如下:
//赋值运算符既是移动赋值运算符,也是拷贝赋值运算符
HasPtr& HasPtr::operator=(HasPtr rhs)
{
swap(*this, rhs);
return *this;
}
对于拷贝赋值,首先要拷贝构造rhs(左值),然后swap
对于移动赋值,首先要移动构造rhs(右值),然后swap
都需要进行rhs的构造,性能不高
新的移动赋值运算符和移动构造函数如下
//拷贝赋值运算符
HasPtr& HasPtr::operator=(const HasPtr& rhs)
{
if (this != &rhs)
{
delete ps;
swap(*this, rhs);
}
return *this;
}
//移动赋值运算符
HasPtr& HasPtr::operator=(HasPtr&& rhs)noexcept
{
if (this != &rhs)
{
delete ps;
//移动元素
ps = rhs.ps;
i = rhs.i;
rhs.ps = nullptr;
}
return *this;
}
练习13.54:
运行错误,产生二义性,传入的参数与两个函数都发生匹配