这是我的情况:
我有一门课是这样的:
class JSON {
private:
std::vector<JSON> children;
...
public:
JSON& operator[](std::string propertyName);
...
}
移动语义是如何应用的?如果使用它们会有什么不同吗?
我觉得如果我做
std::swap(json["hello"], json["world"])
2个JSON&对象将像深度复制一样无效,因为JSON类中没有涉及指针。
据我所知,如果我想有效地使用Move Semantics,我需要有一个指向我的子向量的指针来std::毫不费力地移动指针,我说如果我的类没有指针,std::swap就没有用了,这是对的吗?
std::swap在我的情况下会这样做吗?
temp = json1.children; // by value? deep copy of std::vector?
json1.children = json2.children; // by value? deep copy of std::vector?
json2.children = temp; // by value? deep copy of std::vector?
以下是一个简短的上下文:我通过套接字接收文本,将文本转换为JSON,用json["type"]
获取消息的类型,然后我的目标是向订阅该消息类型的订阅者发送包含json["data"]
的std::shared_ptr<const JSON>
。所以我做了这个:
JSON* dummy = new JSON();
std::swap(*dummy, json["data"]);
std::shared_ptr<const JSON> dataPtr(dummy);
如果我不这样做,而只做std::shared_ptr<const JSON>(&json["data"])
,那么超出范围的共享指针和JSON都将尝试释放json["data"]
(程序失败)。如果您执行std::make_shared<JSON>(json["data"])
,则使用JSON&调用复制构造函数;,这是一个深度复制。
我希望我的意图是明确的,我只是想避免深度复制JSON
或std::vector<JSON>
据我所知,如果我想有效地使用Move Semantics,我需要有一个指向我的子向量的指针,以轻松地移动该指针,我说如果我的类没有指针,那么std::swap是无用的吗?
不,这通常不是真的,标准库容器类将有移动构造函数,只要你知道如何调用它们,它们就会做你想做的事情,这就引出了你问题的第二部分:
std::swap在我的情况下会这样做吗?
我知道得到答案";取决于";,但与C++中的大多数东西一样,这确实取决于具体情况。
为了以最通用的方式回答您的问题,是的,std::swap()
可能在大多数时候都会做您想要的事情,尤其是如果您只是使用标准库容器类。事情变得奇怪的地方(我没有足够的信息给你一个完整的答案)是你已经定义了自己的类,这里只显示了其中的一部分。魔鬼在于细节,所以程序的实际行为将取决于这些省略号中的内容。
通常,当您试图理解复制/移动行为的预期时,您确实需要从构造函数的角度进行思考。假设您使用的是";现代的";(即11后版本)的C++,std::swap()
函数大致如下所示:
template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}
另请参阅此相关帖子。更具体地说,对于您的示例,模板实例化将如下所示:
void swap(JSON& t1, JSON& t2) {
JSON temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}
请记住,std::move()
实际上只是一种奇特的方法,可以通过一些角大小写处理将左值引用转换为右值引用。函数本身什么都不做,它是告诉编译器如何执行重载解析的一种方法。
因此,现在的问题变成了:当编译器需要从对对象类型JSON
的右值引用构造JSON对象时,会发生什么?这个问题的答案取决于类中可用的构造函数,其中一些构造函数可能是由编译器隐式生成的。另请参阅本文。
编译器将为操作选择最合适的构造函数,它可能是隐式的,并且根据您在类上声明的内容,实际上可能不是本例中解释的移动构造函数。为了避免陷入这种陷阱,您需要知道右值引用可以绑定到const
左值引用,因此使用具有以下签名的复制构造函数:
JSON(const JSON &);
在某些情况下,是std::move()
操作左侧的有效过载候选者。这可能就是为什么你有时会听到人们说CCD_;实际上并没有移动任何东西";,或者是";仍然只是复制";。
那么,所有这些都会把你的代码留在哪里呢?基本上,如果您没有用户声明的构造函数,并且让编译器为您执行,那么std::swap
可能会以您想要的方式移动所有成员的内存。一旦你开始声明你自己的构造函数,故事就会变得更加复杂,我们必须讨论细节。
作为这里的一个小附言,你真的需要使用swap()
吗?看起来您只是试图为一个已经用另一个对象的内容初始化的对象构造一个shared_ptr
。这可能是一个稍微简单一点的方法:
std::shared_ptr<const JSON> outPtr = std::make_shared<JSON>(std::move(json["data"]));
这将使用移动构造函数构造JSON类型的对象(假设它是我提到的注意事项中最好的重载候选者),并向它返回shared_ptr