在C ++中,对具有唯一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;
}

当前,这是我的代码。在调用代码的多个地方,我希望能够编辑该类的所有属性,惟一ID除外。如果我添加一个属性,则必须对调用代码(在多个位置)进行编辑才能设置该新属性

  1. 除了唯一ID之外,是否有一种允许所有属性都可以被编辑的好方法
  2. 是否可以重载分配并移动运算符以执行此操作,并且仍然删除副本构造函数
  3. 任何人都有一个具有唯一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::moveattribute1上使用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新的“技巧”来进行分配),但我的野心还不够大查看它们以检查是否有与此完全相同的东西。