构造函数,移动语义move与右值引用
构造函数
C++的构造函数包含一般构造函数,拷贝构造函数 与 移动构造函数。
拷贝构造函数
- 其中包含浅拷贝和深拷贝(此处以深拷贝为例),主要是通过将已存在的对象的所有成员拷贝给新对象,来实现对新对象的初始化。这样就会存在两个一样的对象,相当于内存中存在两份。
- 拷贝构造函数的参数是一个左值引用,即为该类对象的常引用。
移动构造函数
- 移动构造函数通过将已存在对象的资源转移给新对象(资源控制权的转移),来实现对新对象的初始化,而不必进行拷贝。此时旧对象资源就会转移给新对象,而旧对象就成为右值,将亡值。此时内存中该对象也只存在一份。
- 移动构造函数的形参是一个右值引用,其形式为
T&&
形式,其中T
为类型。 - 对于使用临时的对象来构造新的对象,且后续不会再使用该临时对象,此时更适合使用移动构造函数。因为移动构造函数让新对象直接接管和使用临时对象的资源以及内存空间,来实现初始化,避免了重新分配内存空间进拷贝操作,提高了新对象的初始化效率,同时节省不必要的内存。
- 可以通过
std::move()
函数来实现对移动构造函数的调用,因为该函数可以将左值变成右值。
class A
{
public:
// 构造函数
A(int val1)
{
printf("Constructor......\n");
num1 = val1;
}
// 拷贝构造函数
A(const A& obj): num1(obj.num1)
{
printf("Copy Constructor +++++\n");
}
// 移动构造函数,其参数为右值引用,调用该函数时需要提供右值
A(A&& obj)
{
printf("Move Constructor ====\n");
num1 = obj.num1;
}
private:
int num1;
};
int main()
{
int val = 10; // val为左值
A a1(val); // 调用构造函数
A a2 = a1; // 调用拷贝构造函数,a2与a1一样,a1相当于a2的一个副本
A a4(a1); // 调用拷贝构造函数,传入的参数相当于对象的左值引用
A a4(std::move(a2));// 调用移动构造函数, a2的资源将被转移给a4,避免拷贝副本
}
运行结果如下图所示,根据传入的参数类型,来调用对应的构造函数。
std::move() 函数
std::move()
函数主要是用来实现移动语义,可以将左值转换成右值,从而便于调用移动构造函数来实现对新对象的初始化。其本身只能将左值转换成右值,以便右值引用能指向左值。
STL中的容器大部分都已经有移动构造函数的实现,以vector::push_back
为例,其有拷贝构造函数和移动构造函数两种实现,如下图:

int main()
{
string str1 = "hello just a test";
vector<string> vec;
// 调用拷贝构造函数
vec.push_back(str1);
std::cout <<"Copy push, str1 = "<< std::quoted(str1) << endl;
// 调用移动构造函数后,旧对象str1将被释放
vec.push_back(std::move(str1));
std::cout << "Move push, str1 = " << std::quoted(str1) << endl;
}
结果如下图,在调用拷贝构造函数之后,相当于Copy了一个副本,此时 str
中任然是之前的值。而调用移动构造函数之后,旧对象str
的资源及所有权都转移到新的对象中,在此处即为vector容器的元素,然后旧对象str
作为一个将亡值就被释放了,此时打印的旧对象值就是空的。
【注意】
std::move
函数本身只是将左值转换成右值引用, 除此之外不会做其他的事情,如释放旧对象。旧对象的释放是对应的移动构造函数来实现的,如上面的string
容器对象 str1 在调用移动构造函数之后被释放,是由其移动构造函数来做的。如下面的基本类型int
变量 a 被std::move
之后,其值仍然存在,是因为没有对应的移动构造函数来将其释放。且std::move
只是将其从左值转换成右值引用,并没有改变a的类型或移动其内容。所以std::move
一般需要搭配对应的移动构造函数才能更有意义。
int main()
{
int a = 10; // a 为左值
int& lVal = a; // 左值引用指向左值
int&& rVal = std::move(a); // std::move将左值转换成右值引用类型,以便被右值引用所指向。
// a 只是从左值转换成了右值引用类型,但是其本身还是存在的,因为没有移动构造函数将其释放
cout << "a = " << a << endl;
}
【参考资料】
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2022-08-05 计算代码运行时间
2022-08-05 UWP 查看指定文件或目录是否存在