C++ 在C++;,对具有唯一id的对象执行复制/移动/分配的正确方法是什么?
目前,这是我的代码。在调用代码的多个位置,我希望能够编辑该类的所有属性,唯一的_id除外。如果我添加另一个属性,则必须编辑调用代码(在多个位置),才能设置该新属性C++ 在C++;,对具有唯一id的对象执行复制/移动/分配的正确方法是什么?,c++,copy-constructor,move-semantics,uniqueidentifier,assignment-operator,C++,Copy Constructor,Move Semantics,Uniqueidentifier,Assignment Operator,目前,这是我的代码。在调用代码的多个位置,我希望能够编辑该类的所有属性,唯一的_id除外。如果我添加另一个属性,则必须编辑调用代码(在多个位置),才能设置该新属性 是否有一种好方法允许编辑除唯一id之外的所有属性 是否可以重载赋值并移动运算符来执行此操作,同时仍然删除复制构造函数 谁有一个具有唯一ID的类的好例子 是否有一种好方法允许编辑除唯一id之外的所有属性 只需确保id是私有的,并且永远不会返回对它的非常量指针/引用。确保成员函数不修改id应该很简单 是否可以重载赋值并移动运算符来执行此操
std::unique_ptr
本质上就是这样一个类的例子。“id”表示析构函数清理的某个唯一资源。如果每个对象都有一个唯一的、永久的标识,那么您可能无法真正支持复制——对象的副本应该与原始对象相同,在这种情况下,您说每个对象都应该是唯一的,因此拥有一个副本可能没有意义
另一方面,搬迁建设应该更加合理。尽管ID从一个对象移动到另一个对象,但在给定的时间内,仍然只有一个对象具有给定的标识
这个动作非常简单和直接。只需像往常一样在成员初始值设定项列表中初始化您的成员,包括这样一个事实,即由于它是一个移动向量,您希望从源中的唯一_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;
}
理论上,您可能应该在attribute1
和attribute2
上使用std::move
,但由于它们是int
s,所以通常不会产生任何实际的差异
然后我们来讨论一个非常重要的问题:移动赋值。因为我们已经将唯一的\u id
定义为常量
,所以我们不能仅仅复制它。要执行此任务,我们首先销毁目标对象的当前内容,然后使用placement new从源到目标执行移动构造:
Example(Example&& other)
: unique_id(std::move(other.unique_id))
, attribute1(other.attribute1)
, attribute2(other.attribute2)
{
}
这是因为const
资格认证在破坏或施工期间无效,因此这基本上是我们执行任务的唯一方法
下面是一个示例,演示了这一切:
Example &operator=(Example&& other) {
this->~Example();
new (this) Example(std::move(other));
return *this;
}
#包括
#包括
#包括
课例{
公众:
示例(const std::string&unique_id_uu、int attribute1_uu、int attribute2_uu):
唯一标识(唯一标识)、属性1(属性1)、属性2(属性2){};
无效集_attribute1(int attribute1_417;{attribute1=attribute1_417;
无效集_attribute2(int attribute2_u2;){attribute2=attribute2_2;;}
/*删除复制/移动/分配运算符以使每个实例唯一*/
示例(const-Example&)=删除;
示例和运算符=(示例和)=删除;
示例(示例和其他)
:unique_id(std::move(other.unique_id))
,attribute1(其他.attribute1)
,attribute2(其他.attribute2)
{
}
示例和运算符=(示例和其他){
这个->~示例();
新(此)示例(std::move(other));
归还*这个;
}
私人:
const std::字符串唯一\u id;
int属性1;
int属性2;
friend std::ostream&operator这听起来像是一个.Astd::unordered_map
似乎是另一个选项,这样您就不需要示例中的任何unique_id
内容了。对于类中显示的数据成员,您不需要显式重载移动构造函数或移动赋值操作符。DECL将它们声明为default
。复制函数可以声明为delete
,以防止复制。我建议您少考虑类属性,多考虑行为。让所需的类成员由实现行为的需要决定。您的代码是否已经演示了如何完成#1?您提供了ded成员函数用于设置除唯一\u id
之外的每个属性。这允许编辑除唯一id之外的所有属性。为什么这不足以实现您的真正目标?将您的唯一id和相关功能封装到一个专用类中,该类具有定制的构造函数、赋值运算符等以及访问器但没有设置程序(防止暴露ID数据以供外部代码修改)。然后,该类可以是基类,也可以是具有其他属性的类的成员。然后,编译器生成的复制/移动构造函数、赋值运算符等将根据需要进行操作。或者,如果需要,您可以将示例
类设为单例。
#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";
}