一、构造函数和析构函数
构造函数:主要作用于创建函数时对对象成员的属性赋值。
析构函数:主要作用于在对象销毁前,执行一些清理工作(如释放new开辟在堆区的空间)
析构函数不可以有参数,因此不可以发生重载
构造函数可以有参数,因此可以发生重载。
默认无参构造和有参构造
class Person
{
public:
int age;
int *m_hight;
Person()
{
cout << " Person() " << endl;
this->age = 20;
this->m_hight = new int(189);
}
Person(int age,int h)
{
cout << " Person(int age,int * h) " << endl;
this->age = age;
this->m_hight = new int(h);
}
};
int main()
{
Person p;
cout << p.age <<" "<<*p.m_hight << endl;
cout << "------------------" << endl;
Person p2(20,178);
cout << p2.age << " " << *p2.m_hight << endl;
system("pause");
return 0;
}
拷贝构造:
Person(const Person &p)
{
cout << " Person(int age,int * h) " << endl;
this->age = p.age;
this->m_hight = new int(*p.m_hight);
}
代码:
二、移动构造
移动构造是C++11标准中提供的一种新的构造方法。
先举个生活例子,你有一本书,你不想看,但我很想看,那么我有哪些方法可以让我能看这本书?有两种做法,一种是你直接把书交给我,另一种是我去买一些稿纸来,然后照着你这本书一字一句抄到稿纸上。
显然,第二种方法很浪费时间,但这正是有些深拷贝构造函数的做法,而移动构造函数便能像第一种做法一样省时,第一种做法在 C++ 中叫做完美转发。
在C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。
而现在在某些情况下,我们没有必要复制对象,只需要移动它们。
C++11引入移动语义:
源对象资源的控制权全部交给目标对象。
拷贝构造和移动拷贝的区别:
拷贝构造函数中,对于指针,我们一定要采用深层复制,而移动构造函数中,对于指针,我们采用浅层复制
先上一个代码:
#if 1// 移动构造和move
#include<cstring>
#include<cstdlib>
#include<vector>
using namespace std;
int main()
{
string st = "I love xing";
vector<string> vcs;
vcs.push_back(move(st));
cout << vcs[0] << endl;
if (!st.empty())
{
cout << st << endl;
}
cout << "-----------------------------------------------------" << endl;
string str = "I love xing";
vector<string> vc;
vc.push_back(str);
cout << vc[0] << endl;
if (!str.empty())
{
cout << str << " -------" << endl;
}
system("pause");
return 0;
}
#endif
第一段代码使用了move语句,而第二段代码没有使用move语句。输出的结果差异也很明显,第一段代码中,原来的字符串st已经为空,而第二段代码中,原来的字符串st的内容没有变化
浅拷贝深拷贝
#include <iostream>
#include <string>
using namespace std;
class Integer
{
private:
int* m_ptr;
public:
Integer(int value)
: m_ptr(new int(value)) {
cout << "Call Integer(int value)有参" << endl;
}
//参数为左值引用的浅拷贝构造函数,转移堆内存资源所有权,改变 source.m_ptr的值 为nullptr
Integer(Integer& source): m_ptr(source.m_ptr)
{
source.m_ptr = nullptr;
cout << "Integer(Integer& source): m_ptr(source.m_ptr) 浅拷贝" << endl;
}
//参数为常量左值引用的深拷贝构造函数,不改变 source.ptr_ 的值
Integer(const Integer& source) : m_ptr(new int(*source.m_ptr))
{
cout << "Integer(const Integer& source) : m_ptr(new int(*source.m_ptr)) 深度拷贝" << endl;
}
~Integer()
{
cout << "Call ~Integer()析构" << endl;
delete m_ptr;
}
int GetValue(void) { return *m_ptr; }
};
Integer getNum()
{
Integer a(100);
return a;
}
int main()
{
Integer a(getNum());
// getNum 返回的只是一个临时变量 是一个右值
cout << "a=" << a.GetValue() << endl;
cout << "-----------------" << endl;
Integer temp(10010);
cout << "-----------------" << endl;
Integer b(temp);
cout << "b=" << b.GetValue() << endl;
cout << "-----------------" << endl;
system("pause");
return 0;
}
添加移动构造:
// 移动 构造
Integer(Integer&& source): m_ptr(source.m_ptr)
{
source.m_ptr = nullptr;
cout << "Integer(Integer&& source): m_ptr(source.m_ptr) 移动构造" << endl;
}
拷贝构造函数的时候,切记要注意指针的浅层复制
移动构造完整代码
#include <iostream>
#include <string>
using namespace std;
class Integer
{
private:
int* m_ptr;
public:
Integer(int value)
: m_ptr(new int(value)) {
cout << "Call Integer(int value)有参" << endl;
}
//参数为左值引用的浅拷贝构造函数,转移堆内存资源所有权,改变 source.m_ptr的值 为nullptr
Integer(Integer& source): m_ptr(source.m_ptr)
{
source.m_ptr = nullptr;
cout << "Integer(Integer& source): m_ptr(source.m_ptr) 浅拷贝" << endl;
}
//参数为常量左值引用的深拷贝构造函数,不改变 source.ptr_ 的值
Integer(const Integer& source) : m_ptr(new int(*source.m_ptr))
{
cout << "Integer(const Integer& source) : m_ptr(new int(*source.m_ptr)) 深度拷贝" << endl;
}
// 移动 构造
Integer(Integer&& source): m_ptr(source.m_ptr)
{
source.m_ptr = nullptr;
cout << "Integer(Integer&& source): m_ptr(source.m_ptr) 移动构造" << endl;
}
~Integer()
{
cout << "Call ~Integer()析构" << endl;
delete m_ptr;
}
int GetValue(void) { return *m_ptr; }
};
Integer getNum()
{
Integer a(100);
return a;
}
int main()
{
Integer a(getNum());
// getNum 返回的只是一个临时变量 是一个右值
//Integer c(Integer(1000)); // 不会调用移动构造
cout << "a=" << a.GetValue() << endl;
cout << "-----------------" << endl;
Integer temp(10010);
cout << "-----------------" << endl;
Integer b(temp);
cout << "b=" << b.GetValue() << endl;
cout << "-----------------" << endl;
system("pause");
return 0;
}
getNum()函数中需要返回的是一个局部变量,因此它此时就是一个临时变量,因为在函数结束后它就消亡了,对应的其动态内存也会被析构掉,所以系统在执行return函数之前,需要再生成一个临时对象将a中的数据内容返回到被调的主函数中,此处自然就有两种解决方法:1、调用复制构造函数进行备份;2、使用移动构造函数把即将消亡的且仍需要用到的这部分内存的所有权进行转移,手动延长它的生命周期
在返回主函数的过程中,编译器会很智能帮你选择类内合适的构造函数去执行,如果没有移动构造函数,它只能默认的选择复制构造函数,而同时存在移动构造函数和复制构造函数则自然会优先选择移动构造函数
结论:
移动构造函数首先将传递参数的内存地址空间接管,然后将内部所有指针设置为nullptr,并且在原地址上进行新对象的构造,最后调用原对象的的析构函数,这样做既不会产生额外的拷贝开销,也不会给新对象分配内存空间。即提高程序的执行效率,节省内存消耗。
测试移动构造
class A
{
public :
int a;
A(int x):a(x)
{
cout << " A(int x):a(x) " << endl;
}
A(const A &x) :a(x.a)
{
cout << "A(const A &x) :a(x.a) 拷贝构造" << endl;
}
A& operator=( A &x)
{
a = x.a;
cout << "A& operator=( A &x) operator " << endl;
return *this;
}
A(A&& a):a(a.a) // 移动构造
{
cout << " A(A&& a):a(a.a) // 移动构造 " << endl;
}
A& operator=(A &&x)
{
a = x.a;
cout << "A& operator=(A &&x) operator " << endl;
return *this;
}
};
main ()
int main()
{
A a(1);
cout << "-调用有参数构造 --" << endl;
A b = a;
cout << " 调用拷贝构造函数 " << endl;
A c(a);
cout << "-调用拷贝构造函数====== -" << endl;
b = a;
cout << "调用拷贝赋值运算 " << endl;
A e = std::move(a);
cout << "创建对象e ,将a 的值给e ,但是这里使用move 将左值 a 转换为右值 ,所以调用的是移动构造 ---" << endl;
}
三、std::move
std::move() 确实把左值 temp 转换为右值。
通过std::move,可以避免不必要的拷贝操作。
std::move是为性能而生。
测试move
cout << "---------测试 move ---------" << endl;
string s1 = "apple";
string s2 = "banana";
s1 = move(s2);
cout << s2 << " sss " <<s1<< endl;
vector<int> v1;
vector<int> v2 = { 1, 2, 3, 4 };
v1 = move(v2); //从v2转移到v1
//v1=={1, 2, 3, 4}
cout << "v2 是不是空的 "<<v2.size() <<" " <<v1.size()<< endl;
system("pause");
return 0;