本文转载自:https://blog.csdn.net/xuwqiang1994/article/details/79924310
1.概述
没有C++11之前我都这样写代码的
Class* ptr = new Class(xxx);
感觉指针用的挺好的,逻辑清晰,很容易理解。
C++11出来之后,有了"移动语义"和"新的右值引用"的概念,可以预见以后看代码有多头疼。
2.左值与右值
左值(lvalue):代表一个在内存中占有确定位置的对象;
右值(rvalue):通过排他性来定义,不是lvalue的都属于rvalue,即在不在内存中占有确定位置的表达式;
2.1 例1:
int var;
var = 4;
4 = var; //ERROR!
(var + 10) = 4; //ERROR!
如上, 显然"4、(var + 10)"这些表达式是不能作为左值的。
2.2 例2:
int foo() { return 2; }
int main()
{
foo() = 2; //ERROR
return 0;
}
这里的foo()也不能作为左值,而C++中的引用(reference)让这成为可能
2.3 例3:
int globalvar = 20;
int& foo()
{
return globalvar;
}
int main()
{
foo() = 10;
return 0;
}
这里的"&"当表示引用含义的时候,修饰的需要是一个左值。
3.不用指针胜似指针的做法
3.1 疑问
拿C++11举例,C++11里引入了thread,下面的代码会不会让你感到疑惑?
std::thread threads[5];
for (int i=0; i<5; ++i)
threads[i] = std::thread(pause_thread,i+1);
请问thread的构造函数到底被调用了几次呢?
3.2 样例1
我们不考虑thread,看下面这个Intvec代码样例:
class Intvec
{
public:
explicit Intvec(size_t num = 0)
: m_size(num), m_data(new int[m_size])
{
log("constructor");
}
~Intvec()
{
log("destructor");
if (m_data) {
delete[] m_data;
m_data = 0;
}
}
Intvec(const Intvec& other)
: m_size(other.m_size), m_data(new int[m_size])
{
log("copy constructor");
for (size_t i = 0; i < m_size; ++i)
m_data[i] = other.m_data[i];
}
Intvec& operator=(const Intvec& other)
{
log("copy assignment operator");
Intvec tmp(other);
std::swap(m_size, tmp.m_size);
std::swap(m_data, tmp.m_data);
return *this;
}
private:
void log(const char* msg)
{
cout << "[" << this << "] " << msg << "
";
}
size_t m_size;
int* m_data;
};
int main()
{
Intvec v2;
cout << "assigning lvalue...
";
v2 = Intvec(20);
cout << "ended assigning lvalue...
";
return 0;
}
运行结果
$ g++ main2.cpp -o main2 --std=c++11
$ ./main2
[0x7ffe204edc50] constructor
assigning lvalue...
[0x7ffe204edc60] constructor
[0x7ffe204edc50] copy assignment operator
[0x7ffe204edc20] copy constructor
[0x7ffe204edc20] destructor
[0x7ffe204edc60] destructor
ended assigning lvalue...
[0x7ffe204edc50] destructor
结果调用3次构造函数:
Intvec v2;
Intvec(20);
Intvec tmp(other);
3.3 样例2:
我们再加一个成员函数;(不用删除原来的operator=)
Intvec& operator=(Intvec&& other)
{
log("move assignment operator");
std::swap(m_size, other.m_size);
std::swap(m_data, other.m_data);
return *this;
}
运行结果:
$ g++ main2.cpp -o main2 --std=c++11
$ ./main2
[0x7ffe5aa0ad70] constructor
assigning lvalue...
[0x7ffe5aa0ad80] constructor
[0x7ffe5aa0ad70] move assignment operator
[0x7ffe5aa0ad80] destructor
ended assigning lvalue...
[0x7ffe5aa0ad70] destructor
结果只调用了两次构造函数。
从外观上看
Intvec& operator=(Intvec&& other)和
Intvec& operator=(const Intvec& other)
在传参上并没有什么不同。但显然编译器知道自己该调用哪个函数。
4.总结
"&&"就是C++11支持的"新右值引用操作符",operator=(Intvec&& other)这个函数就是实现"移动语义"的一种方法。
PS:C++越改越像个脚本语言,图啥?
从个人角度看,以后写代码,我还是倾向于使用
Intvec* p2 = new Intvec(20);
delete p2;
的方式,这只调用一次构造函数,而且逻辑还很清晰,不用考虑类内部的实现。
Intvec v2 = Intvec(20); //也只调用一次,这就是另外一回事了。
本文转载自:https://blog.csdn.net/xuwqiang1994/article/details/79924310