本节介绍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();
}