问题描述
/* 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;
}
当前,这是我的代码。在调用代码的多个地方,我希望能够编辑该类的所有属性,惟一ID除外。如果我添加另一个属性,则必须对调用代码(在多个位置)进行编辑才能设置该新属性。
解决方法
除了唯一ID之外,是否有一种允许所有属性都可以被编辑的好方法?
只要确保id是私有的,就永远不要返回非const指针/对其的引用。确保成员函数不修改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)
{
}
理论上,您可能还应该在std::move
和attribute1
上使用attribute2
,但是由于它们是int
,因此通常不会产生任何真实效果差异。
然后我们得到一个不平凡的东西:移动分配。由于我们已将unique_id
定义为const
,因此我们不能仅仅复制它。为了完成这项工作,我们首先销毁目标对象的当前内容,然后使用new放置从源到目标的移动构造:
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)),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成员的人,它们使用相同的destroy / placement新的“技巧”来进行分配),但我的野心还不够大查看它们以检查是否有与此完全相同的东西。