没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
移动语义与智能指针
移动语义
为什么要用移动语义?
我们回顾一下之前模拟的String.cc
c
l
a
ss
S
tring
{
pu
b
li
c
:
S
tring
():
_
str
(
new
c
h
a
r
[1]())
{}
S
tring
(
c
onst
c
h
a
r
*
pstr
):
_
str
(
new
c
h
a
r
[
strlen
(
pstr
)
+
1]())
{
str
c
py
(
_
str
,
pstr
);}
S
tring
(
c
onst
S
tring
&
rhs
)
:
_
str
(
new
c
h
a
r
[
strlen
(
rhs
.
c
_
str
())
+
1]())
{
str
c
py
(
_
str
,
rhs
.
c
_
str
());
}
S
tring
&
oper
a
tor
=
(
c
onst
S
tring
&
rhs
){
if
(
this
!
=
&
rhs
){
delete
[]
_
str
;
_
str
=
new
c
h
a
r
[
strlen
(
rhs
.
c
_
str
())
+
1];
str
c
py
(
_
str
,
rhs
.
c
_
str
());
}
return
*
this
;
}
~
S
tring
(){
if
(
_
str
){
delete
[]
_
str
;
_
str
=
nullptr
;
}
}
priv
a
te
:
c
h
a
r
*
_
str
;
};
void test
0(){
S
tring str
(
"
hello
"
);
//
拷贝构造
创建s3的过程中实际创建了一个临时对象,也会在堆空间上申请一片空间,然后把字符串
内容复制给s3的pstr,这一行结束时临时对象的生命周期结束,它申请的那片空间被回
收。这片空间申请了,又马上被回收,实际上可以视作一种不必要的开销。我们希望能够
少new一次,可以直接将s3能够复用临时对象申请的空间。
左值与右值
左值和右值是针对表达式而言的,左值是指表达式执行结束后依然存在的持久对象,右值
是指表达式执行结束后就不再存在的临时对象。那如何进行区分呢?其实也简单,能对表
达式取地址的,称为左值;不能取地址的,称为右值。
在实际使用过程中,字面值常量、匿名对象(临时对象)、匿名变量(临时变量),都称
为右值。右值又被称为即将被销毁的对象。
字面值常量,也就是10, 20这样的数字,属于右值,不能取地址。
字符串常量,“world",是属于左值的,位于内存中的文字常量区
试试看下面这些取址操作和引用绑定操作是否可行:
S
tring s
2
=
s
1;
//
先构造,再拷贝构造
//
利用
"
hello
"
这个字符串创建了一个临时对象
//
并复制给了
s
3
//
这一步实际上
new
了两次
S
tring s
3
= "
hello
"
;
}
void test
1()
{
int
a
=
1,
b
=
2;
&
a
;
&
b
;
&
(
a
+
b
);
&
10;
&
S
tring
(
"
hello
"
);
//
非
c
onst
引用尝试绑定
int
&
r
1
=
a
;
int
&
r
2
=
1;
//
c
onst
引用尝试绑定
c
onst int
&
r
3
=
1;
c
onst int
&
r
4
=
a
;
如上定义的
int
&
r
1
和
c
onst
int
&
r
3
叫作左值引用与const左值引用
非const左值引用只能绑定到左值,不能绑定到右值,也就是非const左值引用只能识
别出左值。
const左值引用既可以绑定到左值,也可以绑定到右值,也就是表明const左值引用不
能区分是左值还是右值。
——希望能够区分出右值,并且还要进行绑定
就是为了实现String s3 = "hello"的空间复用需求。
右值引用
C++11提出了新特性右值引用
右值引用不能绑定到左值,但是可以绑定到右值,也就是右值引用可以识别出右值
右值引用本身是左值还是右值?
—— 对r_ref取地址是可行的,r_ref本身是一个左值。但这并不代表右值引用本身一定
是左值。
S
tring s
1(
"
hello
"
);
S
tring s
2(
"
w
a
ngd
a
o
"
);
&
s
1;
&
s
2;
&
(
s
1
+
s
2);
}
//
非
c
onst
引用不能绑定右值
int
&
r
1
=
a
;
int
&
r
2
=
1;
//
error
//
c
onst
引用既可以绑定左值,又可以绑定右值
c
onst int
&
r
3
=
1;
c
onst int
&
r
4
=
a
;
//
右值引用只能绑定右值
int
&&
r
_
ref
=
10;
int
&&
r
_
ref
2
=
a
;
//
error
实际上,右值引用既可以是左值(比如:作为函数的参数、有名字的变量),也可以
是右值(函数的返回类型)
这个问题,我们留到1.1.6章节再做讨论。
移动构造函数(重要)
有了右值引用后,实际上再接收临时对象作为参数时就可以分辨出来。
之前String str1 = String("hello");这种操作调用的是拷贝构造函数,形参为const String &
类型,既能绑定右值又能绑定左值。为了确保右值的复制不出错,拷贝构造的参数设为
const引用;为了确保进行左值的复制时不出错,一律采用重新开辟空间的方式。有了能够
分辨出右值的右值引用之后,我们就可以定义一个新的构造函数了 —— 移动构造函数。
给String类加上移动构造函数,在初始化列表中完成浅拷贝,使s3的pstr指向临时对象的
pstr所指向的空间(复用),还不能忘记要将右操作数(临时对象)的pstr设为空指针,因
为这个临时对象会马上销毁(要避免临时对象调用析构函数回收掉这片堆空间)
再运行代码,发现String s3 = "hello";
加上编译器的去优化参数 -fno-elide-constructors
发现没有再调用拷贝构造函数,而是调用了移动构造函数。
移动构造函数的特点:
1.移动构造函数优于拷贝构造函数执行(实际上绑定左值也会经历这个过程,但是移
动构造函数中的右值引用不能绑定左值,所以采用了拷贝构造函数)
2.移动构造函数如果不显式写出,编译器不会自动生成。
移动赋值函数(重要)
有了移动构造函数的成功经验,很容易想到原本的赋值运算符函数。
比如,我们进行如下操作时
原本赋值运算符函数的做法
S
tring
(
S
tring
&&
rhs
)
:
_
pstr
(
rhs
.
_
pstr
)
{
c
out
<< "
S
tring
(
S
tring
&&
)
" <<
endl
;
rhs
.
_
pstr
=
nullptr
;
}
S
tring s
3(
"
hello
"
);
s
3
=
S
tring
(
"
w
a
ngd
a
o
"
);
剩余31页未读,继续阅读
资源评论
向日葵.
- 粉丝: 286
- 资源: 19
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功