C++ 11 中的move赋值运算符
移动赋值是一种将资源(如内存、文件等)从一个对象转移到另一个对象而不复制它们的方法。
例子:
cpp
#include <iostream>
#include <vector>
// for std::move
#include <utility>
int main() {
std::vector<int> a = {1, 2, 3, 4};
std::vector<int> b;
// move assignment!
b = std::move(a);
// a is now empty
std::cout << "a.size() = " << a.size() << "\n";
// b has the data
std::cout << "b.size() = " << b.size() << "\n";
}
输出:
bash
a.size() = 0
b.size() = 4
移动赋值运算符
这是一个特殊函数,允许一个对象从另一个对象获取资源的所有权,而无需复制。
用户自定义移动赋值运算符
程序员可以自定义移动赋值运算符。
例子:
cpp
#include <iostream>
#include <cstring>
class MyString {
char* data;
public:
// Constructor
MyString(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
// User-defined move assignment operator
MyString& operator=(MyString&& other) {
std::cout << "Move assignment called\n";
if (this != &other) {
// Free old memory
delete[] data;
// Steal the pointer
data = other.data;
// Set source to null
other.data = nullptr;
}
return *this;
}
// Destructor
~MyString() {
delete[] data;
}
void print() const {
if (data)
std::cout << data << "\n";
else
std::cout << "[empty]\n";
}
};
int main() {
MyString a("Hello");
MyString b("World");
b = std::move(a);
b.print();
a.print();
}
输出:
bash
Move assignment called
Hello
[empty]
移动赋值运算符的必要性
-
提升性能 - 移动赋值运算符通过转移资源而非复制资源来提高速度,尤其对于大型对象而言。
-
减少内存占用 - 它通过重用现有资源避免在内存中创建不必要的重复项。
-
高效处理临时对象 - 移动赋值运算符允许对象从临时值中获取数据所有权,而无需进行代价高昂的复制操作。
自己实现字符串move拷贝构造和move赋值运算符重载
示例
cpp
#include <iostream>
// - [https://www.youtube.com/@TheCherno](https://www.youtube.com/@TheCherno)
// ## C++基础
// - [lvalues and rvalues in C++](https://www.youtube.com/watch?v=fbYknr-HPYE)
// - [std::move and the Move Assignment Operator in C++](https://www.youtube.com/watch?v=OWNeCTd7yQE)
// - [Move Semantics in C++](https://www.youtube.com/watch?v=ehMg6zvXuMY)
// - [SMART POINTERS in C++ (std::unique_ptr, std::shared_ptr, std::weak_ptr)](https://www.youtube.com/watch?v=UOB7-B2MfwA)
// - [Weak Pointers in C++ (std::weak_ptr)](https://www.youtube.com/watch?v=M0GLQEfplxs)
class MyString
{
public:
MyString() = default;
MyString(const char* s)
{
std::cout << "Created!" << std::endl;
m_Size = strlen(s);
m_Data = new char[m_Size];
memcpy(m_Data, s, m_Size);
}
MyString(const MyString& other)
: m_Size(other.m_Size)
{
std::cout << "Copied!" << std::endl;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
}
MyString(MyString&& other) noexcept
{
std::cout << "Moved!" << std::endl;
m_Data = other.m_Data;
m_Size = other.m_Size;
other.m_Data = nullptr;
other.m_Size = 0;
}
MyString& operator=(const MyString& other) noexcept
{
std::cout << "Assigned!" << std::endl;
if (this == &other)
return *this;
delete[] m_Data;
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
return *this;
}
MyString& operator=(MyString&& other) noexcept
{
std::cout << "Move Assigned!" << std::endl;
if (this == &other)
return *this;
delete[] m_Data;
m_Data = other.m_Data;
m_Size = other.m_Size;
other.m_Data = nullptr;
other.m_Size = 0;
return *this;
}
~MyString()
{
std::cout << "Destroyed!" << std::endl;
delete[] m_Data;
}
void Print() const
{
for (unsigned int i = 0; i < m_Size; i++)
std::cout << m_Data[i];
std::cout << std::endl;
}
private:
char* m_Data = nullptr;
unsigned int m_Size = 0;
};
class Entity
{
public:
Entity(const MyString& name)
: m_Name(name)
{
}
Entity(MyString&& name)
: m_Name(std::move(name))
{
}
private:
MyString m_Name;
};
int main()
{
{
MyString hello = "Hello World!";
Entity e1(hello); // Copy
Entity e2(std::move(hello)); // Move
}
std::cout << "---------------------" << std::endl;
MyString apple = "Apple";
MyString dest;
std::cout << "Apple: ";
apple.Print();
std::cout << "Dest: ";
dest.Print();
dest = std::move(apple); // Move Assignment
std::cout << "Apple: ";
apple.Print();
std::cout << "Dest: ";
dest.Print();
return 0;
}
输出
bash
Created!
Copied!
Moved!
Destroyed!
Destroyed!
Destroyed!
---------------------
Created!
Apple: Apple
Dest:
Move Assigned!
Apple:
Dest: Apple
Destroyed!
Destroyed!