@CrazyHenry
2018-02-23T11:39:38.000000Z
字数 1636
阅读 1212
ccccC++Primer
- Author:李英民 | Henry
- E-mail: li
_
yingmin@
outlookdot
com- Home: https://liyingmin.wixsite.com/henry
快速了解我: About Me
转载请保留上述引用内容,谢谢配合!
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <typeinfo>
#include <numeric>
#include <memory>
using namespace std;
//win+<-/-> 切换窗口位置
//ctrl+win+<-/->切换桌面
//ctrl+alt+上/下 改变显示器方向
class A
{
public:
A(){}
A(const A& a1)//这里一定要加上const,否则编译出错
{
cout<<"调用拷贝构造函数"<<endl;
}
A(A&& a2)
{
cout<<"调用移动构造函数"<<endl;
}
};
A func1(A a) //1
{
A *pl = new A(a); //1
return *pl;//1
}
int main()
{
A a;//no
A b = a;//1
A *p = new A(b);//1
*p = a;//no
A arr[2] = {a,b};//2
A d = func1(b); //+3=7
return 0;
}
一共会调用7次拷贝构造函数。
为什么A(const A& a1)
这里一定要加上const,否则编译出错?
答案是,func1(b)
的返回结果虽然是个A类对象,但是却是个右值,右值是无法获取其地址的,因此,将一个右值绑定到引用的行为是非法的!所以就无法调用拷贝构造,因为拷贝构造函数的参数是一个引用。但是如果参数改为const A&
,情况就不一样了!可以接受const对象,字面量,和需要类型转换的对象!
A d = func1(b);
这句代码只调用了3次,是引用编译器的返回值优化。传入参数一次,函数体一次,调用点和初始化一共调用1次。用调用点的返回值直接当做=的左侧对象,所以最后一次拷贝赋值或者移动赋值都不会发生!也就是说,编译器会把最后函数返回给调用点的初始化方式直接用在左侧对象上,如果函数返回时用拷贝构造,那么就只有一次拷贝构造,如果函数返回时用移动构造,就只有一次移动构造!
修改类定义:
A(A& a1)//去掉const
{
cout<<"调用拷贝构造函数"<<endl;
}
A(A&& a2)//移动构造函数
{
cout<<"调用移动构造函数"<<endl;
}
int main()
{
A a;//no
A b = a;//1
A *p = new A(b);//1
//*p = a;//no
A arr[2] = {a,b};//2
A d = std::move(func1(b)); //+3=7 +1 = 8
return 0;
}
这里我们显式避免了编译器的返回值初始化优化,返回的右值仍然可以被显式返回右值引用,然后调用移动构造函数!
会发现,最后会调用一次移动构造函数来构造d。
再把函数返回改成使用移动构造:
class A
{
public:
A(){}
A(A& a1)//这里一定要加上const,否则编译出错
{
cout<<"调用拷贝构造函数"<<endl;
}
A(A&& a2) noexcept
{
cout<<"调用移动构造函数"<<endl;
}
};
A&& func1(A a) //1
{
A *pl = new A(a); //1
return std::move(*pl);//1
}
int main()
{
A a;//no
A b = a;//1
A *p = new A(b);//1
//*p = a;//no
A arr[2] = {a,b};//2
A d = func1(b); //+3=7 最后一次使用移动构造
A e = A();//这个会被编译器优化,直接用默认构造函数初始化e
return 0;
}