【C++ Primer 第13章】6.对象移动
右值引用
左值和右值
(1)两者区别:
①左值:能对表达式取地址、或具名对象/变量。一般指表达式结束后依然存在的持久对象。
②右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不再存在的临时对象。
总结:一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。
(2)右值的分类
①将亡值(xvalue,eXpiring value):指生命期即将结束的值,一般是跟右值引用相关的表达式,这样表达式通常是将要被移动的对象,如返回类型为T&&的函数返回值(如std::move)、经类型转换为右值引用的对象(如static_cast<T&&>(obj))、xvalue类对象的成员访问表达式也是一个xvalue(如Test().memberdata,注意Test()是个临时对象)
②纯右值(prvalue, PureRvalue):按值返回的临时对象、运算表达式产生的临时变对象、原始字面量和lambda表达式等。
(3)C++11中的表达式
标准库的 move 函数
• 虽然不能将一个右值直接绑定到一个左值上,但可以显式地将一个左值转换为对应的右值引用类型。我们可以通过调用一个名为 move的新标准库函数来获得绑定到左值上的引用。头文件utility。
int &&rr3 = std::move(rr1); //ok
• 我们可以销毁一个移后源对象,也可以赋予它新值,但不能使用一个移后源对象的值。
移动构造函数和移动赋值运算符
• 与拷贝函数不同,移动构函数不分配任何新的内存,它接管给定StrVec中的内存,在接管内存之后, 它将给的对象中的指针置为nullptr,这样就完成了从给定对象的移动操作,此对象将继续存在。
#include<iostream>
#include<string>
#include<memory>
using namespace std; class StrVec {
public:
StrVec(): elements(nullptr), first_free(nullptr), cap(nullptr) {}
StrVec(const StrVec &);
StrVec& operator=(const StrVec&);
~StrVec() { free(); }; StrVec(StrVec &&s) noexcept;
StrVec& opearator=(StrVec &&rhs) noexcept; void push_back (const string&);
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
string *begin() const { return elements; }
string *end() const { return first_free; } private:
static allocator<string> alloc;
void chk_n_alloc() { if (size() == capacity()) reallocate(); }
pair<string*, string*> alloc_n_copy(const string*, const string *);
void free();
void reallocate();
string *elements; //指向数组首元素的指针
string *first_free; //指向数组第一个空闲元素的指针
string *cap; //指向数组尾后位置的指针
}; StrVec::StrVec(const StrVec &s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
} StrVec& StrVec::operator=(const StrVec &rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = newdata.second;
return *this;
} void StrVec::free()
{
if (elements)
{
for (auto p = first_free; p != elements; )
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
} pair<string *, string *> StrVec::alloc_n_copy(const string *b, const string *e)
{
auto data = alloc.allocate(e - b);
return { data, uninitialized_copy(b, e, data) };
} void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
} void StrVec::reallocate()
{
auto newcapacity = size() ? * size() : ;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements; //原对象的elements指针
for (size_t i = ; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
} StrVec::StrVec(StrVec &&s) noexcept
: elements(s.elements), first_free(s.first_free), cap(s.cap)
{
s.elements = s.first_free = cap = nulllptr;
} StrVec& StrVec::StrVec(StrVec &&s) noexcept
{
if (this = &s)
{
free();
elemens = rhs.elements;
first_free = .frhsirst_free;
cap = rhs.cap;
rhs.elements = srhs.first_free = rhs.cap = nullptr;
}
return *this;
}
• 与拷贝操作不同,编译器根本不会为某些类合成移动操作,特别是,如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或析构函数、编译器根本就不会为它合成移动构造函数。
• 如果一个类有一个可用的拷贝构造函数而没有移动构造函数,则其对象是通过拷贝构造函数来“移动”的,拷贝赋值运算符和移动赋值运算符的情况类似。
拷贝并交换赋值运算符和移动操作
右值引用和成员函数
• 区分移动和拷贝的重载函数通常有一个版本接受一个const T&, 而另一个版本接受一个T&&。
作为一个具体例子,我们将为StrVec类定义另一个版本的push_back:
class StrVec {
public:
void push_back(const std::string&); //拷贝元素
void push_back(std:: string&&) //移动元素
// 其他成员的定义, 如前
}; void StrVec::push_back(const string &s)
{
chk_n_alloc(); //确保有空间容纳新的元素
//在first_free指向的元素中构造s的一个副本
alloc.construct(first_free++, s);
} void StrVec::push_back(string &&s)
{
chk_n_alloc(); // 如果需要的话为StrVec重新分配内存
alloc.construct(first_free++, std::move(s));
}
右值和左值引用成员函数
#include <iostream>
using namespace std; class Foo {
public:
Foo & operator=(const Foo&) &; //只能向可修改的左值赋值
Foo anotherMem() const &; //正确,const 限定符在前 Foo gg() const &&; //this 也可以指向一个右值
}; Foo& Foo::operator=(const Foo &rhs) & //引用限定符和const一样必须同时出现在声明和定义中
{
//执行将rhs赋予本对象所需的工作
return *this;
} Foo& retFoo() //返回一个左值
{
return *(new Foo());
} Foo retVal() //返回一个右值
{
return Foo();
} int main(void)
{
Foo i, j;
i = j;
retFoo() = j; //正确,retFoo() 返回一个左值
i = retVal(); //正确,我们可以将一个右值作为赋值运算符右侧运算对象
return ;
}
重载和引用函数
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; class Foo{
public:
Foo sorted() &&; //可用于改变的右值
Foo sorted() const &; //可用于任何类型的Foo
// Foo sorted(); //错误,当有两个以上同名且同参数列表的成员函数时, // 要么全(指这些同名且同参数列表的成员函数)都加引用限定符(只要有就行, 不论右值还是左值引用),要么都不加引用限定符
Foo sorted(int);//正确,参数列表不同 private:
vector<int> data;
}; Foo Foo::sorted() && // 本对象为右值,因此可以原址排序
{
sort(data.begin(), data.end());
return *this;
} Foo Foo::sorted() const & // 本对象是const或是一个左值,哪种情况我们都不能原址排序 {
Foo ret(*this); // 拷贝一个副本
sort(ret.data.begin(), ret.data.end()); // 副本排序
return ret; // 返回副本
} Foo& retFoo() //返回一个左值
{
return *(new Foo());
} Foo retVal() //返回一个右值
{
return Foo();
} int main(void)
{
retVal().sorted(); //retVal() 返回一个右值,调用 Foo::sorted() &&
retFoo().sorted(); //retFoo() 返回一个左值,调用 Foo::sorted() const &
return ;
}
参考资料
【1】右值引用(1)_基本概念
【C++ Primer 第13章】6.对象移动的更多相关文章
- 【C++ Primer 第13章】2. 拷贝控制和资源管理
拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...
- [C++ Primer] : 第13章: 拷贝控制
拷贝, 赋值与销毁 当定义一个类时, 我们显示地或隐式地指定在此类型的对象拷贝, 移动, 赋值和销毁时做什么. 一个类通过定义5种特殊的成员函数来控制这些操作, 包括: 拷贝构造函数, 拷贝赋值运算符 ...
- 【C++ Primer 第13章】1. 拷贝控制、赋值和销毁
拷贝控制.赋值和销毁 如果一个构造函数的第一个参数是自身类的引用,且额外的参数都有默认值,则此构造函数是拷贝控制函数(拷贝构造函数不应该是explicit的). 如果我们没有为一个类定义拷贝构造函数, ...
- 【C++ Primer 第13章】5. 动态内存管理类
StrVec类的设计 [题目描述]:我们将实现标准库vector类的一个简化版本,我们所做的一个简化是不使用模板,我们类只用于string,因此,它被命名为StrVec. #include<io ...
- 【C++ Primer 第13章】3. 交换操作
交换操作 class HasPtr { friend void swap(HasPtr &rhs, HasPtr &yhs); //其他成员定义 }; void swap(HasPtr ...
- C++ primer plus读书笔记——第13章 类继承
第13章 类继承 1. 如果购买厂商的C库,除非厂商提供库函数的源代码,否则您将无法根据自己的需求,对函数进行扩展或修改.但如果是类库,只要其提供了类方法的头文件和编译后的代码,仍可以使用库中的类派生 ...
- Linux就这个范儿 第13章 打通任督二脉
Linux就这个范儿 第13章 打通任督二脉 0111010110……你有没有想过,数据从看得见或看不见的线缆上飞来飞去,是怎么实现的呢?数据传输业务的未来又在哪里?在前面两章中我们学习了Linux网 ...
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
- Java核心技术卷一基础技术-第13章-集合-读书笔记
第13章 集合 本章内容: * 集合接口 * 具体的集合 * 集合框架 * 算法 * 遗留的集合 13.1 集合接口 Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制. 13. ...
随机推荐
- jq实现对checkbox的常用操作
场景1:点击checkbox 其他checkbox被选中的都取消 (意思是只能选中一个checkbox) function fn(obj,event){ //obj就是这个this这个对象 ,eve ...
- JQuery弹出层,点击按钮后弹出遮罩层,有关闭按钮【转】
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...
- HITS算法--从原理到实现
本文介绍HITS算法的相关内容. 1.算法来源 2.算法原理 3.算法证明 4.算法实现 4.1 基于迭代法的简单实现 4.2 MapReduce实现 5.HITS算法的缺点 6.写在最后 参考资料 ...
- 七、uboot 代码流程分析---C环境建立
7.1 start.S 修改 在上一节中的流程中,发现初始化的过程并没由设置看门狗,也未进行中断屏蔽 如果看门狗不禁用,会导致系统反复重启,因此需要在初始化的时候禁用看门狗:中断屏蔽保证启动过程中不出 ...
- luogu 1993 小K的农场
差分约束+spfa判负环 dfs判负环 #include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i+ ...
- C++ error LNK2001
1.没有函数的实现部分 2.函数的头文件和实现部分不一致 3.缺库LIB.DLL 4.库H文件函数和库体不一致
- Django 2.0 学习(12):Django 模板语法
Django 模板语法 一.模板 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法 模板语法变量:{{ }} 在Django模板中遍历复杂数据结构的关键是句点字 ...
- Linux 内核里的“智能指针”【转】
转自:http://blog.jobbole.com/88279/ 众所周知,C/C++语言本身并不支持垃圾回收机制,虽然语言本身具有极高的灵活性,但是当遇到大型的项目时,繁琐的内存管理往往让人痛苦异 ...
- 一个python脚本解决安装mq的依赖问题
import tarfile import sys,os from _utils.patrol2 import run_cmd import zipfile def get_version(filen ...
- 有pom.xml文件但是无法用maven构建问题
java项目转maven项目,要注意pom.xml文件中是否定义了JDK的版本,要与环境保持一致.项目,右键,configure,选择转换为maven项目即可.转换后,有三个位置需要注意: 1.Jav ...