
目录
C++11新增了两个类的默认成员函数:移动构造函数和移动赋值运算符重载。
如果你没有手动实现移动构造函数,并且析构函数、拷贝构造、赋值运算符重载均没有手动实现,编译器就会帮你生成一个默认的移动构造函数。默认生成的移动构造函数,对于内置类型进行按字节的值拷贝,对于自定义类型,将会去调用它的移动构造函数,如果这个内置类型并没有移动构造函数,则编译器去调用它的拷贝构造函数。
如果你没有手动实现移动赋值运算符重载,并且析构函数、拷贝构造、赋值运算符重载均没有手动实现,编译器就会帮你生成一个默认的移动赋值运算符重载。默认生成的移动赋值运算符重载,对于内置类型进行按字节的值拷贝,对于自定义类型,将会去调用它的移动赋值运算符重载,如果这个内置类型并没有移动赋值运算符重载,则编译器去调用它的赋值运算符重载。
C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化。
Person(Person&& p)=default;//让编译器强制生成移动构造
例如现在需要禁用某个类的拷贝构造函数,C++98的玩法是将这个类的拷贝构造函数加上私有权限,并且只声明不实现,就可以在类的内部和外部禁用掉拷贝构造。
C++11使用delete关键字禁止生成某个默认构造函数:
A(const A& a)=delete;//防拷贝
变量和解引用得到的变量是左值,我们可以获取它的地址,左值可以出现赋值符号的左边和右边,右值只能出现在赋值符号的右边。
int main()
{
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;
return 0;
}
1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值(权限平移)
int main()
{
// 左值引用只能引用左值,不能引用右值。
int a = 10;
int& ra1 = a; // ra为a的别名
//int& ra2 = 10; // 编译失败,因为10是右值
// const左值引用既可引用左值,也可引用右值。
const int& ra3 = 10;
const int& ra4 = a;
return 0;
}
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。C++11中将右值划分成纯右值(内置类型表达式的值)和将亡值(自定义类型的表达式的值)
int main()
{
double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
return 0;
}
1. 右值引用只能引用右值,不能引用左值。
2. 但是右值引用可以引用move以后的左值。
int main()
{
// 右值引用只能右值,不能引用左值。
int&& r1 = 10;
// error C2440: “初始化”: 无法从“int”转换为“int &&”
// message : 无法将左值绑定到右值引用
int a = 10;
int&& r2 = a;
// 右值引用可以引用move以后的左值
int&& r3 = std::move(a);
return 0;
}
右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1,这里的rr1是左值。如果不想rr1被修改,可以用const int&& rr1 去引用。(这个特性在移动赋值中也可以体现,移动赋值参数是右值引用,通过交换这个右值引用的对象完成赋值的目的)
int main()
{
double x = 1.1, y = 2.2;
int&& rr1 = 10;//rr1其实是一个左值
const double&& rr2 = x + y;
rr1 = 20;
rr2 = 5.5;//报错
return 0;
}
因为右值引用的形参是左值,层层传递时,需要将这个形参move为右值:
void push_back(T&& x)//x为左值
{
insert(end(),move(x));//需要用move转换为右值,不然会去调用左值版本的insert
}
iterator insert(interator pos,T&& x)//x为左值
{
node* newnode=new node(move(x));//需要用move转换为右值,不然会去调用左值版本的node构造函数
}
list_node(T&& x)//x是左值
:_next(nullptr)
,_prev(nullptr)
,_data(move(x))//需要用move转换为右值,不然会去调用左值版本的T _data的构造函数
{}
不过这样写的话需要左值写一份,右值的写一份,代码重复了。更推荐下面讲的完美转发的写法。


模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用到完美转发。
std::forward 完美转发在传参的过程中保留对象原生类型属性,当然完美转发也是需要在模板的环境下才能生效,如果不是模板,T&& t同样会被识别成右值引用。
万能引用和完美转发必须保证传参时,才实例化对象,如果传参前模板已经被实例化了,将构不成万能引用和完美转发。

使用完美转发修改本节2.1中的move写法:凡是需要往下一层传参的,需要保持右值属性的都需要使用完美转发。
template <class T>
void push_back(T&& x)//x可以接收任意类型
{
insert(end(),std::forward<T>(x));
}
iterator insert(interator pos,T&& x)///x可以接收任意类型
{
node* newnode=new node(std::forward<T>(x));
}
list_node(T&& x)//x可以接收任意类型
:_next(nullptr)
,_prev(nullptr)
,_data(std::forward<T>(x))
{}
左值引用在函数传参、函数传返回值(必须保证返回值出了作用域还在)时使用可以减少拷贝。如果函数的返回值出了作用域会被销毁,则不能使用左值引用来减少拷贝(销毁后引用的对象非法),这个时候就需要使用右值引用了。C++在没有右值引用的时候,可以采用输出型参数的方法解决传值返回多次拷贝的问题。
使用场景一:深拷贝的类中的传值返回的拷贝问题
C++11通过右值引用实现的移动构造和移动赋值解决值返回多次拷贝的问题,提升了效率:
class string
{
public:
//拷贝构造
string(const string& s)//传入的s是左值,需要老老实实构造
{
string tmp(s.c_str());
swap(tmp);
}
//移动构造
string(string&& s)//传入的s是将亡值,可以换走s的成员
{
swap(s);//将s的成员直接换给*this,s消亡后自动调用析构函数
}
// 赋值重载
string& operator=(const string& s)
{
string tmp(s);
swap(tmp);
return *this;
}
// 移动赋值
string& operator=(string&& s)
{
swap(s);
return *this;
}
private:
char* _str = nullptr;
size_t _capacity = 0;
size_t _size = 0;
};
对于深拷贝的类,C++11使用右值引用实现的移动构造和移动赋值,在需要拷贝的场景中,可以直接转移将亡值的资源。如下图,虽然to_string是传值返回,但是str被编译器识别为将亡值,编译器会调用移动构造一步到位的对ret1进行构造,代价很低。

使用场景二:插入右值数据,也可以减少拷贝
int main()
{
list<string> it;
string s1("1111");
it.push_back(s1);//左值,深拷贝
it.push_back(string("2222"));//右值,直接移动构造转移右值资源
it.push_back("3333");//右值,直接移动构造转移右值资源
return 0;
}

【内容简介】
Git是一款让人一开始觉得很容易学,但却很难精通的工具。本书除了介绍Git的相关知识外,还会模拟各种常见的状况,让读者知道应该在什么时候使用什么指令。
《Git从入门到精通》共分11个章节,1~3章介绍安装工具及环境,对于已经安装完成的读者可直接从第4章开始阅读。第5章介绍Git基本的使用方式,虽然难度不高,但却是整个Git系统的基础。第6章介绍Git中常用的分支功能以及使用情境,第7~9章则是介绍如何修改现有的历史记录、使用标签,以及如何应对其他常见的状况。
前面的内容都是在自己的计算机上就可以完成的,从第10章开始介绍如何将自己计算机里的记录推一份到线上(GitHub)。*后一章(第11章)介绍团队开发时可能会使用的开发过程Git Flow。
市面上的参考书籍或网络教程大多是教大家如何通过终端机指令来学习Git,这让不少想学习Git的新手打了退堂鼓。本书除了教大家如何在终端机视窗中输入Git指令,还搭配了图形界面工具,缓和了读者的学习曲线,让读者更容易上手。
京东自营购买链接:
《Git从入门到精通》(高见龙)【摘要 书评 试读】- 京东图书
送书活动
本期书籍为《Git从入门到精通》送一本
开奖时间:2023-04-09-23:59
参与方式:
1.点赞收藏文章
2.在评论区留言:人生苦短,我用Git(留言最多五条)
3.抽奖方式为评论区留言随机抽奖(C语言rand()函数随机抽取),会在评论区如期公布中奖者,包邮到家。
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin
我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我
如thisanswer中所述,Array.new(size,object)创建一个数组,其中size引用相同的object。hash=Hash.newa=Array.new(2,hash)a[0]['cat']='feline'a#=>[{"cat"=>"feline"},{"cat"=>"feline"}]a[1]['cat']='Felix'a#=>[{"cat"=>"Felix"},{"cat"=>"Felix"}]为什么Ruby会这样做,而不是对object进行dup或clone? 最佳答案 因为那是thedocumenta
我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da
假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum
我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat
代码:threads=[]Thread.abort_on_exception=truebegin#throwexceptionsinthreadssowecanseethemthreadseputs"EXCEPTION:#{e.inspect}"puts"MESSAGE:#{e.message}"end崩溃:.rvm/gems/ruby-2.1.3@req/gems/activesupport-4.1.5/lib/active_support/dependencies.rb:478:inload_missing_constant':自动加载常量MyClass时检测到循环依赖稍加研究后,
我正在跟踪我们的应用程序(ruby2.1)中的内存泄漏问题。我正在使用这两种技术:ObjectSpace.dump_all将所有对象转储到JSON流,然后进行离线分析。我使用的第二种技术是使用ObjectSpace.reachable_objects_from进行实时分析。在这两种方式中,我发现我泄漏的对象被一个对象RubyVM::Env引用。任何人都可以向我解释什么是RubyVM::Env。如何删除这些引用? 最佳答案 RubyVM::Env是一个包含变量引用的内部ruby类。这是我的测试:require'objspace'a
我目前有一个运行Ruby1.8.7和Rails2.3.2的RubyonRails项目我有一些从数据库中读取数据的单元测试,特别是两个连续项目的日期时间列,这两个项目应该相隔24小时。在一项测试中,我将项目2的日期时间设置为与项目1的日期时间相同。当我执行断言以确保两个值相等时,测试在rails2.3.2下工作正常。当我升级到rails2.3.11时,测试失败显示两次之间的差异将关闭并出现以下错误:expectedbutwas.这两个版本的rails中似乎存在浮点转换问题。如何解决float问题? 最佳答案 这也发生在我身上,我最终这