Bootstrap

【每日 C/C++ 问题】

一、什么是 C++ 中的移动语义?它的作用是什么?

  •         什么是 C++ 中的移动语义呢?简单来说,移动语义允许我们将资源从一个对象有效地转移到另一个对象,而不是进行传统的拷贝操作,特别是对于大型对象或动态分配的资源,可以避免不必要的拷贝操作,减少内存的分配和释放的开销。想象一下,我们有一个大型的对象,比如一个包含大量数据的容器。如果我们使用拷贝语义来复制这个对象,可能会涉及大量的内存分配和数据复制,这是非常耗时和消耗资源的。但是,通过移动语义,我们可以直接将资源的所有权从一个对象转移到另一个对象,而无需进行昂贵的拷贝操作。
  •         那么,移动语义的作用是什么呢?首先,它可以极大地提高程序的性能。特别是在处理大型对象或者频繁进行对象复制的情况下,移动语义可以避免不必要的拷贝操作,从而节省时间和内存。其次,移动语义有助于更好地管理资源。例如,对于动态分配的内存或者其他系统资源,移动语义可以确保资源的正确转移和释放,避免资源泄漏和重复释放的问题。此外,移动语义还可以使代码更加简洁和易于维护。通过使用移动语义,我们可以避免复杂的拷贝构造和赋值操作,使代码更加清晰和直观。
  •         移动语义在 C++ 中有几个关键的实现方式。其中,右值引用是实现移动语义的重要工具。通过右值引用,我们可以识别出临时对象或者即将被销毁的对象,并对其进行资源的转移操作。例如,在移动构造函数和移动赋值运算符中,我们可以利用右值引用来实现资源的高效转移。

二、右值引用是什么?如何使用右值引用实现移动语义?

C++11 增加了一个新的类型,右值引用 记作: &&。

左值是指在内存中有明确的地址,我们可以找到这块地址的数据(可取地址)。

右值只是提供数据,无法找到地址(不可取地址)。

所有有名字的变量都是左值,而右值是匿名的。

右值引用:

  •        右值引用就是对右值的引用类型。因为右值是匿名的,所以我们只能通过引用的方式找到它。无论是左值引用还是右值引用都必须被初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用,该右值所占的内存就可以被找到并且使用。
// 移动构造函数实现的移动语义
#include <iostream>
using namespace std;
class Test
{
public:
	Test() : m_num(new int(100))
	{
		cout << "构造函数" << endl;
	}
	Test(const Test& a) : m_num(new int(*a.m_num))
	{
		cout << "拷贝构造函数" << endl;
	}

	// 添加移动构造函数,参数是右值引用
	Test(Test&& a) : m_num(a.m_num)
	{
		a.m_num = nullptr;
		cout << "移动构造函数" << endl;
	}

	~Test()
	{
		delete m_num;
		cout << "析构函数" << endl;
	}

	int* m_num;
};

Test getObj()
{
	Test t;
	return t;
}
int main()
{
	Test t = getObj(); // 因为 getObj 返回的是右值,所以调用移动构造函数
	cout << "t.m_num" << *t.m_num << endl;
	return 0;
}

;