1)移动构造函数和移动赋值运算符的使用案例,代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
class B
{
public:
B():b_(20) //无参构造函数
{
//cout << "b constructor" << endl;
}
B(const B& obj): b_(obj.b_)
{
//cout << "b copy constructor" << endl;
}
virtual ~B()
{
//cout << "b deconstructor---" << endl;
}
public:
int b_;
};
class A
{
public:
A():m_pb(new B()){
cout << "a--- constructor" << endl; }
A(const A& obj):m_pb(new B(*obj.m_pb)) //老师的正确写法
{
//m_pb = obj.m_pb; 自己写的,是错的
cout << "class A copy constructor" << endl;
}
//如果加入移动构造函数,就不会调用copy构造函数了
A(A&& obj)noexcept:m_pb(obj.m_pb) //参数中不能有const!!! noexcept位置不能错
{
obj.m_pb = nullptr; //打断原来的指针跟老对象的关系
cout << "A&&--- move constructor---" << endl;
}
//拷贝赋值运算符
A& operator=(const A& src)
{
if (this == &src)
{
return *this;
}
delete m_pb;
m_pb = new B(*src.m_pb);
std::cout << "class A move_assgin operator " << endl;
return *this;
}
virtual ~A()
{
delete m_pb;
cout << "A--- deconstructor---" << endl;
}
private:
B* m_pb;
};
//定义一个全局函数
A getA()
{
A a;
return a; //因为返回值返回,生成临时对象;这时,如果有移动构造函数,优先调用;没有则调用拷贝构造函数;
}
int main()
{
//测试B类
//B* pb = new B();
//pb->b_ = 19;
//B* pb2 = new B(*pb); //调用拷贝构造函数
//delete pb; //没有这句话,就不会调用析构函数
//delete pb2;
//A a = getA();
//A a1(a); //调用拷贝构造函数,因为a是左值
//A a2(std::move(a));
//A&& a3(std::move(a)); //没有影响,相当于给a起个别名a3
//A a4 = getA();
//A a2;
//a2 = std::move(a4);//移动赋值运算符演示
return 0;
}
2)左值引用与右值引用的学习,谁不会都可以问,保证给你讲明白
一)引用的学习:
1)const的引用就可以绑定到右值; 如const int& a = 10; //这样是可以的
系统做了两件事情,等价于两条语句: int tmp = 10; (先定义一个临时变量)const int& a = tmp; 把临时变量绑定到引用;
2)右值引用,就是绑定右值;
作用:用来绑定一些即将销毁或者一些临时对象上;
3)左值引用绑左值,右值引用绑右值;
int a = 10; //const的引用既可以绑定左值,又可以绑定右值,左右通吃;
const int& b = a; //绑定左值
const int& c = 10; //绑定右值
int&& aa = 20; //虽然aa是右值引用,但本身是左值,所以,int& bb = aa;这条语句是合法的;
前置递增递减运算符为左值:
int i = 100;
++i = 200;
cout << i << endl;
后置递增递减运算符(如i++)为右值:因为i++先产生一个临时变量tmp;用完i后,i++;返回的是临时变量tmp;
int main() //i++测试代码
{
int i = 10;
int&& b = i++; //绑定的是右值,没有绑定i,所以b的值和i没有任何关系!!!
cout << b << endl; //10
b = 40;
cout << b << endl; //40
cout << i << endl; //11
return 0;
}
4)任何函数的形参都是左值;
5)右值引用的引入目的:
就是为了提高程序的运行效率;
6)std::move 根本没有移动的操作,就一个目的:把左值转换为右值;这样:以前定义的左值和右值引用就穿一条裤子了;
int main()
{
int a = 10;
cout << a << endl; //10
//下面这条语句,相当于b和a是一个变量了(b是a的别名);
int&& b = std::move(a); //int&& b = a; 这样写就不可以
a = 30;
cout << b << endl; //40
cout << a << endl; //40
return 0;
}
string st = "ABC";
string def = std::move(st); //执行的是string的移动构造函数,没有执行std::move函数,相当于创建一个对象;
string&& def = std::move(st); //执行了std::move函数;def和st穿一条裤子;
一)临时对象的学习:
1) 三种情况产生临时对象:
a)(传值方式)函数参数传递;可以看出来产生了临时对象
b) 类型转换生成的临时对象;看不出来产生了临时对象;
测试如:class Test{
public: int x = 20}; Time test; test = 1000;
//test = 1000; 干了三个事:
1)用1000数字创建了一个临时对象;
2)调用拷贝赋值运算符,把这个临时对象的各个成员值赋给test对象;
3)销毁这个临时创建的Test对象;
改进:Time test = 1000; //定义时初始化,这个概念必须记住!!!
C++语言只会为const引用(如:const string& rsource)产生临时变量;
c)函数返回临时对象
解决办法:直接返回临时对象, return Test(ts.bal*2);
2)类外运算符重载;