提问人:Alex 提问时间:11/2/2020 更新时间:11/2/2020 访问量:400
在 C++ 中,对具有唯一 ID 的对象进行复制/移动/赋值的正确方法是什么?
In C++, what is the proper way to do copy/move/assignment for objects with a unique id?
问:
/* Example.h file */
class Example {
public:
Example(const std::string& unique_id_, int attribute1_, int attribute2_,):
unique_id(unique_id_), attribute1(attribute1_), attribute2(attribute2_){};
void set_attribute1(int attribute1_){ attribute1 = attribute1_; }
void set_attribute2(int attribute2_){ attribute2 = attribute2_; }
/* Deleting copy/move/assignment operators to make each instance unique */
Exercise_Data(const Exercise_Data&) = delete;
Exercise_Data(Exercise_Data&&) = delete;
Exercise_Data& operator= (Exercise_Data&) = delete;
Exercise_Data& operator= (Exercise_Data&&) = delete;
private:
const std::string unique_id;
int attribute1;
int attribute2;
}
目前,这是我的代码。在调用代码的多个位置,我希望能够编辑此类的所有属性,但unique_id除外。如果我添加另一个属性,则必须编辑调用代码(在多个位置)才能设置该新属性。
- 有没有一种好方法可以允许编辑除唯一 ID 之外的所有属性?
- 是否可以重载赋值和移动运算符来执行此操作,同时仍然删除复制构造函数?
- 有人有一个很好的具有唯一 ID 的类的例子吗?
答:
有没有一种好方法可以允许编辑除唯一 ID 之外的所有属性?
只要确保 id 是私有的,并且您永远不会返回对它的非常量指针/引用。确保成员函数不修改 id 应该是微不足道的。
是否可以重载赋值和移动运算符来执行此操作,同时仍然删除复制构造函数?
类可以移动,但不可复制。此类类型称为仅移动。
如果要保留成员 const,则无法实现移动操作。我建议不要使用const成员。
有人有一个很好的具有唯一 ID 的类的例子吗?
std::unique_ptr
本质上是此类的一个例子。“id”表示析构函数清理的一些唯一资源。
如果每个对象都有一个唯一的、永久的身份,那么你可能就不能真正支持复制了——一个对象的副本应该与原始对象相同,在这种情况下,你说每个对象都应该是唯一的,所以拥有一个副本可能没有意义。
另一方面,搬家建设应该更合理。尽管 ID 从一个对象移动到另一个对象,但在给定时间仍然只有一个对象具有给定的身份。
移动 ctor 非常简单明了。只需像往常一样在成员初始值设定项列表中初始化您的成员,包括由于它是一个移动 ctor,因此您希望从源代码中的unique_id移动:
Example(Example&& other)
: unique_id(std::move(other.unique_id))
, attribute1(other.attribute1)
, attribute2(other.attribute2)
{
}
从理论上讲,您可能也应该使用 on 和 ,但由于它们是 s,因此通常不会产生任何真正的区别。std::move
attribute1
attribute2
int
然后我们进入一个有点不平凡的问题:移动任务。既然我们已经定义为 ,我们就不能只是复制它。为了完成这项工作,我们首先销毁目标对象的当前内容,然后使用 placement new 执行从源到目标的移动构造:unique_id
const
Example &operator=(Example&& other) {
this->~Example();
new (this) Example(std::move(other));
return *this;
}
这之所以有效,是因为资格认证在破坏或建造过程中没有影响,所以这基本上是我们实现任务的唯一方法。const
下面是一个演示其全部操作的示例:
#include <string>
#include <new>
#include <iostream>
class Example {
public:
Example(const std::string& unique_id_, int attribute1_, int attribute2_):
unique_id(unique_id_), attribute1(attribute1_), attribute2(attribute2_){};
void set_attribute1(int attribute1_){ attribute1 = attribute1_; }
void set_attribute2(int attribute2_){ attribute2 = attribute2_; }
/* Deleting copy/move/assignment operators to make each instance unique */
Example(const Example&) = delete;
Example& operator= (Example&) = delete;
Example(Example&& other)
: unique_id(std::move(other.unique_id))
, attribute1(other.attribute1)
, attribute2(other.attribute2)
{
}
Example &operator=(Example&& other) {
this->~Example();
new (this) Example(std::move(other));
return *this;
}
private:
const std::string unique_id;
int attribute1;
int attribute2;
friend std::ostream &operator<<(std::ostream &os, Example const &e) {
return os << "ID: " << e.unique_id;
}
};
int main() {
Example a("A", 1, 2);
std::cout << "A: " << a << "\n";
Example b{std::move(a)}; // move construction
std::cout << "B: " << b << "\n";
Example c("B", 3, 4); // construct a destination object
c = std::move(b); // move assign into it
std::cout << "C: " << c << "\n";
}
至于对象中唯一 ID 的真实示例......如果不回去搜索代码,我不确定。我能记得一些看起来它们可能至少非常接近的(当然还有其他有 const 成员的,它们使用相同的销毁/放置新“技巧”来执行任务),但我没有足够的野心来浏览它们以检查是否有任何与此完全相同的内容。
评论
std::unordered_map<unique_id, Example>
unique_id
Example
default
delete
unique_id
Example