C++学习笔记(五十):c++ 移动语义与std::move

本节介绍c++的移动语义与新特性std::move,本节介绍的内容主要用在性能优化上。

c++移动语义和c++左值右值的内容相关联,可以先看之前发的左值右值内容。

不使用移动语义的代码如下:

#include 
class String
{
public:
	String() = default;
	String(const char* date)
	{
		std::cout << "Created!\n";
		m_Data = new char[strlen(date)+1];
		m_Size = strlen(date);
		memcpy(m_Data, date, m_Size+1);
	}
	String(const String& other)
	{
		std::cout << "Copityed!\n";
		m_Data = new char[other.m_Size + 1];  //需要在堆上分配,所以会影响性能,移动语义可以不在堆上进行分配
		m_Size = other.m_Size;
		memcpy(m_Data, other.m_Data, m_Size + 1);

	}

	~String()
	{
		delete[] m_Data;
	}
	void PrintString()
	{
		for (int i = 0; i < m_Size; i++)
		{
			std::cout << m_Data[i];
		}
		std::cout << "\n";

	}
private:
	char* m_Data;
	uint32_t m_Size;
};
class Entity
{
public:
	Entity(const String& name)
		:m_Name(name)
	{}
	void Print()
	{
		m_Name.PrintString();
	}
private:
	String m_Name;
};

int main()
{
	Entity entity(String("pcop"));
	entity.Print();

	std::cin.get();
}

上述代码执行结果如下:

Created!
Copityed!
pcop

可以看到执行了两次内存分配,一次是在Created中,一次是在Copityed中,因为String("pcop")先创建了一个String对象,然后通过引用的方式传递给Entity,Entity调用复制构造函数,在复制构造函数中新建一个String对象m_Data。

使用移动语义,可以减去String("pcop")创建String对象这一次内存分配,代码如下:

#include 
class String
{
public:
	String() = default;
	String(const char* date)
	{
		std::cout << "Created!\n";
		m_Data = new char[strlen(date)+1];
		m_Size = strlen(date);
		memcpy(m_Data, date, m_Size+1);
	}
	String(const String& other)
	{
		std::cout << "Copityed!\n";
		m_Data = new char[other.m_Size + 1];  //需要在堆上分配,所以会影响性能,移动语义可以不在堆上进行分配
		m_Size = other.m_Size;
		memcpy(m_Data, other.m_Data, m_Size + 1);

	}
	String(String&& other) noexcept //传递的是一个右值,临时值
	{
		std::cout << "Moved!\n";
		m_Data = other.m_Data;     //实际进行了一次浅拷贝,m_Data指针指向other.m_Data所指的内存地址
		m_Size = other.m_Size;
		other.m_Size = 0;
		other.m_Data = nullptr;
	}

	~String()
	{
		std::cout << "Destroyed!\n";
		delete[] m_Data;
	}
	void PrintString()
	{
		for (int i = 0; i < m_Size; i++)
		{
			std::cout << m_Data[i];
		}
		std::cout << "\n";

	}
private:
	char* m_Data;
	uint32_t m_Size;
};
class Entity
{
public:
	Entity(const String& name)
		:m_Name(name)
	{}
	Entity(String&& name)
		:m_Name((String&&)name)  
	{}
	void Print()
	{
		m_Name.PrintString();
	}
private:
	String m_Name;
};

int main()
{
	Entity entity(String("pcop"));
	entity.Print();

	std::cin.get();
}

std::move的使用方式及移动赋值运算符,代码如下:

#include 
class String
{
public:
	String() = default;
	String(const char* date)
	{
		std::cout << "Created!\n";
		m_Data = new char[strlen(date)+1];
		m_Size = strlen(date);
		memcpy(m_Data, date, m_Size+1);
	}
	String(const String& other)
	{
		std::cout << "Copityed!\n";
		m_Data = new char[other.m_Size + 1];  //需要在堆上分配,所以会影响性能,移动语义可以不在堆上进行分配
		m_Size = other.m_Size;
		memcpy(m_Data, other.m_Data, m_Size + 1);

	}
	String(String&& other) noexcept //传递的是一个右值,临时值
	{
		std::cout << "Moved!\n";
		m_Data = other.m_Data;     //实际进行了一次浅拷贝,m_Data指针指向other.m_Data所指的内存地址
		m_Size = other.m_Size;
		other.m_Size = 0;
		other.m_Data = nullptr;
	}
	String& operator=(String&& other) noexcept
	{
		std::cout << "Moved!\n";
		if (this != &other)
		{
			delete[] m_Data;
			m_Data = other.m_Data;     //实际进行了一次浅拷贝,m_Data指针指向other.m_Data所指的内存地址
			m_Size = other.m_Size;
			other.m_Size = 0;
			other.m_Data = nullptr;
		}
		return *this;

	}

	~String()
	{
		std::cout << "Destroyed!\n";
		delete[] m_Data;
	}
	void PrintString()
	{
		for (int i = 0; i < m_Size; i++)
		{
			std::cout << m_Data[i];
		}
		std::cout << "\n";

	}
private:
	char* m_Data;
	uint32_t m_Size;
};
class Entity
{
public:
	Entity(const String& name)
		:m_Name(name)
	{}
	Entity(String&& name)
		:m_Name(std::move(name))  
	{}
	void Print()
	{
		m_Name.PrintString();
	}
private:
	String m_Name;
};

int main()
{
	//Entity entity(String("pcop"));
	//entity.Print();

	String s1 = "pcop";
	//String s2 = std::move(s1);  //使用move将s1转换成临时变量,此时=调用的是构造函数,而不是移动赋值运算符

	String s3;
	s3 = std::move(s1);  //此时调用的是移动赋值运算符

	s3.PrintString();

	std::cin.get();
}

你可能感兴趣的:(c++学习笔记,c++,学习,笔记)