我们写代码的时候,总会遇到下面的情况:
class Obj {
};
Obj getObj() {
Obj obj; // 创建临时变量
// some initialize code for obj
return obj; // 返回一个临时变量
}
int main() {
Obj obj = getObj(); // 这里会将返回值赋值给obj
}
这里面会有多个临时变量的创建和销毁。
添加打印信息:
#include <iostream>
class Obj {
public:
Obj() {
std::cout << "Obj()" << std::endl;
}
~Obj() {
std::cout << "~Obj()" << std::endl;
}
Obj(const Obj& obj) {
std::cout << "Obj(const Obj& obj)" << std::endl;
}
Obj& operator=(const Obj& obj) {
std::cout << "Obj& operator=(const Obj& obj)" << std::endl;
}
};
Obj getObj() {
Obj inside_obj;
// some initialize code for obj
return inside_obj;
}
int main() {
Obj obj = getObj();
}
运行结果如下:
huyanjie@debian:~/study/book$ g++ t2.cpp -fno-elide-constructors
huyanjie@debian:~/study/book$ ./a.out
Obj() ----> Obj inside_obj;
Obj(const Obj& obj) ----> return inside_obj; (temp_returned obj = inside_obj)
~Obj() ----> inside_obj destroyed
Obj(const Obj& obj) ----> Obj obj = temp_returned_obj(等价于Obj obj(temp_returned_obj);)
~Obj() ----> temp_returned_obj destroyed
~Obj() ----> obj destroyed
C++11 引入了右值属性的概念。
右值属性:如果一个值是一个临时值,表达式结束后,就没有意义了,那么他具有右值属性,具有右值属性的变量是不能取地址的。
如函数的返回值等。我们可以用右值引用来关联一个具有右值属性的变量: T&& a = temp;
我们可以重载拷贝函数,当变量具有右值属性的时候,调用右值拷贝函数,避免深拷贝。
#include <iostream>
class Obj {
public:
Obj() {
std::cout << "Obj()" << std::endl;
}
~Obj() {
std::cout << "~Obj()" << std::endl;
}
Obj(const Obj& obj) {
std::cout << "Obj(const Obj& obj)" << std::endl;
}
Obj& operator=(const Obj& obj) {
std::cout << "Obj& operator=(const Obj& obj)" << std::endl;
}
Obj(Obj&& obj) {
std::cout << "Obj(Obj&& obj)" << std::endl;
}
Obj& operator=(Obj&& obj) {
std::cout << "Obj& operator=(Obj&& obj)" << std::endl;
}
};
Obj getObj() {
Obj inside_obj;
// some initialize code for obj
return inside_obj;
}
int main() {
Obj obj = getObj();
}
运行结果如下:
huyanjie@debian:~/study/book$ g++ t3.cpp -fno-elide-constructors -std=c++11
huyanjie@debian:~/study/book$ ./a.out
Obj() <span style="font-family:Arial, Helvetica, sans-serif;"> ----> Obj inside_obj;</span>
Obj(Obj&& obj) ---->Obj temp_return_obj(inside_obj); // 因inside_obj具有右值类型
~Obj()
Obj(Obj&& obj) ----> Obj obj(temp_return_obj); // 同样因为temp_return_obj 有右值类型
~Obj()
~Obj()
这样我们在设计类的时候,如果提供一个右值的拷贝,赋值函数,当要拷贝的数据具有右值类型的时候,可以调用右值拷贝,构造函数,避免深拷贝。
如下面一个string的实现:
#include <iostream>
#include <string.h>
class MyString {
public:
MyString() : mData(NULL) {
std::cout << "MyString()" << std::endl;
}
MyString(const char *str) {
std::cout << "MyString(const char *str)" << std::endl;
if (str == NULL) {
mLen = 0;
mData = new char[1];
*mData = '\0';
} else {
mLen = strlen(str);
mData = new char[mLen+1];
strcpy(mData, str);
}
}
~MyString() {
std::cout << "~MyString()" << std::endl;
if (mData != NULL)
delete[] mData;
mLen = 0;
}
MyString(const MyString& str) {
std::cout << "MyString(const MyString& str)" << std::endl;
mLen = str.size();
mData = new char[mLen+1];
strcpy(mData, str.mData);
}
MyString& operator=(const MyString& str) {
std::cout << "MyString& operator=(const MyString& str)" << std::endl;
if (this == &str)
return *this;
delete[] mData;
mLen = str.mLen;
mData = new char[mLen+1];
strcpy(mData, str.mData);
return *this;
}
size_t size() const {
return mLen;
}
MyString(MyString&& str) {
std::cout << "MyString(MyString&& str)" << std::endl;
mData = str.mData;
mLen = str.mLen;
str.mData = NULL;
str.mLen = 0;
}
MyString& operator=(MyString&& str) {
std::cout << "MyString& operator=(MyString&& str)" << std::endl;
mData = str.mData;
mLen = str.mLen;
str.mData = NULL;
str.mLen = 0;
}
friend std::ostream& operator<<(std::ostream& os, const MyString& str);
private:
char *mData;
size_t mLen;
};
std::ostream& operator<<(std::ostream& os, const MyString& str) {
os << str.mData;
return os;
}
MyString getMyString() {
MyString inside_str("123");
// some initialize code for str
return inside_str;
}
int main() {
MyString str = getMyString();
std::cout << "The str is " << str << std::endl;
}
当一个变量作为一个临时变量或者函数的返回值的时候,它具有右值属性,那么怎么样把其他的变量强制转换成具有右值属性呢?
对用 static_cast<T&&>来强制转换,更好的方式是使用std::move(T). 如:
int main() {
MyString str = getMyString();
std::cout << "The str is " << str << std::endl;
MyString str1 = std::move(str);
std::cout << "The str1 is " << str1 << std::endl;
}