本节介绍c++的移动语义与新特性std::move,本节介绍的内容主要用在性能优化上。
c++移动语义和c++左值右值的内容相关联,可以先看之前发的左值右值内容。
不使用移动语义的代码如下:
#include <iostream>
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 <iostream>
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 <iostream>
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();
}