1.c++编译器返回值优化
由于编译器默认开启了返回值优化(RVO/NRVO, RVO, Return Value Optimization 返回值优化,或者NRVO, Named Return Value Optimization)。
虽然各大厂家的编译器都已经都有了这个优化,但是这并不是
c++
标准规定的,而且不是所有的返回值都能够被优化,而这篇文章的主要讲的右值引用,移动语义可以解决编译器无法解决的问题。 为了更好的观察结果,可以在编译的时候加上
-fno-elide-constructors
选项(关闭返回值优化)。
g++ -fno-elide-constructors -std=c++11 -O3 -o main main.cpp
2.拷贝构造函数
#include <iostream>
class A {
public:
A(){std::cout << "A construct." << std::endl;}
~A(){std::cout << "A desconstruct." << std::endl;}
A(const A& a){std::cout << "A copy construct" << std::endl;}
A(A&& a){std::cout << "A right ref construct" << std::endl;}
A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};
int main(int argc, char** argv) {
std::cout << "----------a1-----------" << std::endl;
A a1;
std::cout << "----------a2-----------" << std::endl;
A a2 = a1;
std::cout << "----------a3-----------" << std::endl;
A a3(a1);
}
运行:
----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A copy construct
A desconstruct.
A desconstruct.
A desconstruct.
3. 拷贝赋值
#include <iostream>
class A {
public:
A(){std::cout << "A construct." << std::endl;}
~A(){std::cout << "A desconstruct." << std::endl;}
A(const A& a){std::cout << "A copy construct" << std::endl;}
A(A&& a){std::cout << "A right ref construct" << std::endl;}
A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};
/* 拷贝赋值*/
int main(int argc, char** argv) {
std::cout << "----------a1-----------" << std::endl;
A a1;
std::cout << "----------a2-----------" << std::endl;
A a2;
a2 = a1;
}
运行:
----------a1-----------
A construct.
----------a2-----------
A construct.
A copy and assign.
A desconstruct.
A desconstruct.
4. 右值拷贝构造
#include <iostream>
class A {
public:
A(){std::cout << "A construct." << std::endl;}
~A(){std::cout << "A desconstruct." << std::endl;}
A(const A& a){std::cout << "A copy construct" << std::endl;}
A(A&& a){std::cout << "A right ref construct" << std::endl;}
A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};
A get() {
return A(); //等价于 A a; return a;
}
/* 右值拷贝构造*/
int main(int argc, char** argv) {
std::cout << "----------a1-----------" << std::endl;
A a1;
std::cout << "----------a2-----------" << std::endl;
A a2 = std::move(a1);
std::cout << "----------a3-----------" << std::endl;
A a3 = get();
}
运行:
----------a1-----------
A construct.
----------a2-----------
A right ref construct
----------a3-----------
A construct.
A right ref construct
A desconstruct.
A right ref construct
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
备注:
1. get()函数,首先构造一个A的实例,然后拷贝构造函数返回值临时变量,函数返回值临时变量再构造A对象实例a3; 因为A()和函数返回值临时变量都是临时对象或者将亡值(如:局部变量A a; return a;),所以都是调用右值拷贝构造函数;
2. std::move是将左值无条件转为右值,因此a2也调用拷贝构造函数;
如果没有右值拷贝构造, 运行:
----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A construct.
A copy construct
A desconstruct.
A copy construct
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
5. 右值拷贝赋值
#include <iostream>
class A {
public:
A(){std::cout << "A construct." << std::endl;}
~A(){std::cout << "A desconstruct." << std::endl;}
A(const A& a){std::cout << "A copy construct" << std::endl;}
A(A&& a){std::cout << "A right ref construct" << std::endl;}
A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};
A get() {
return A(); //等价于A a; return a;
}
/* 右值赋值*/
int main(int argc, char** argv) {
std::cout << "----------a1-----------" << std::endl;
A a1;
std::cout << "----------a2-----------" << std::endl;
A a2;
a2 = std::move(a1);
std::cout << "----------a3-----------" << std::endl;
A a3;
a3 = get();
}
运行:
----------a1-----------
A construct.
----------a2-----------
A construct.
A right ref and assign.
----------a3-----------
A construct.
A construct.
A right ref construct
A desconstruct.
A right ref and assign.
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
如果没有右值拷贝赋值, 运行:
----------a1-----------
A construct.
----------a2-----------
A construct.
A copy and assign.
----------a3-----------
A construct.
A construct.
A copy construct
A desconstruct.
A copy and assign.
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
6. 总结:
1. 右值拷贝构造和右值拷贝赋值的目的是提高程序的运行效率;
2. 如果一个类没有右值拷贝构造和右值拷贝赋值函数,那么就调用(左值)拷贝构造函数和(左值)拷贝赋值函数;
3. 编译器优化,会默认开启返回值优化, 对于右值拷贝构造(倒数第二个例子),如果使用默认编译器优化(g++ -std=c++11 -o main main.cpp),运行结果是:
----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A construct.
A desconstruct.
A desconstruct.
A desconstruct.
我们会发现,本来应该的两次拷贝构造,实际执行时一次都没有;