房地产基础知识seo优化专家
以前也看过一些 std::move
的介绍文章,总结下来一句话:std::move
没有任何代价。这个总结实在是坑人。
今天突然悟了,实际应用中悟的。如果写一个相关的教程,完全不应该从 std::move 入手,而是应该从如何降低对象拷贝代价入手。
struct A {int m_a;int m_b;
};
如果要对 A 做拷贝,可以写成下面这样。对于这种情况,就算用了 std::move,也没有什么用。
A a1;
g_a2 = a1; // g_a2 是一个类成员
但是,对于下面这个结构,就不一样了:
struct A {int m_a;int m_b;char *m_s;
};
此时,下面的逻辑取决于 A 的拷贝构造函数实现,一般来说,会把 m_a
, m_b
, 直接赋值,m_s
则会做一个深拷贝。
A a1;
g_a2 = a1; // g_a2 是一个类成员
有些时候,我们不希望做深拷贝。浅拷贝行不行?一般来说是不行的。因为浅拷贝后 g_a2.m_s
和 a1.m_s
会指向同一片内存区域,并且 a1
是栈变量,它析构后会释放 m_s
指向的内存,那么 g_a2
就会得到一个野指针。
怎么办呢?C++ 发明了一种“移动”语义,让 A 实现下面这样一个移动拷贝函数:
struct A {// 默认构造A() : m_a(0), m_b(0.0), m_s(nullptr) {}// 带参数构造A(int a, int b, const std::string& s) : m_a(a), m_b(b), m_s(new std::string(s)) {}~A() { delete m_s; } // 释放内存// 移动构造A(A&& other) {m_a = other.m_a;m_b = other.m_b;m_s = other.m_s;other.m_s = nullptr; // 关键之处,强行删掉 other.m_s 的指针,实现了内存的“移动”}// 移动赋值A& operator=(A&& other) {if (this != &other) { // 防止自赋值m_a = other.m_a;m_b = other.m_b;m_s = other.m_s;other.m_s = nullptr; // 关键之处,强行删掉 other.m_s 的指针,实现了内存的“移动”}return *this;}};
A 还可以这样定义这个函数:
struct A {void set(A&& other) {m_a = other.m_a;m_b = other.m_b;m_s = other.m_s;other.m_s = nullptr;}
};A a1;
g_a2.set(std::move(a1)); // g_a2 是一个类成员
所以,std::move 的确什么事情都不做,它只是提示编译器,去调用支持 move 语义的函数而已,具体做事情的,是这些函数。
Note:STL 中,vector 的 emplace_back、push_back 等也实现了支持 move 语义等版本。