如何使用引用成员和指针向量创建 class?

How do I create a class with reference member and vector of pointers?

我是 C++ 的新手,正在从事个人项目。我想在 C++ 中创建一个 vector<Entity*> entitities,其中每个 Entity 对象都是唯一的。我有一个 class inside header Entity.h 我想创建。现在实体有两个成员变量:

  1. Rectangle rect - Rectangle 类型的对象有四个浮点变量作为成员变量 (x1, y1, x2, y2),它们是矩形两个对角的坐标
  2. vector<Component*> - Base class 类型 Component 的许多不同组件以及一些 Derived classes。 Class Component 的代码如下所示:
/*************************** BASE CLASS **************************/
class Component
{
public:
    virtual ~Component() = default;
    virtual Component* Clone() = 0;
};

/*************************** DERIVED CLASS 1 **************************/
class BasicComponent: public Component
{
private:
    int B= 0;
public:
    BasicComponent* Clone() override {
        return new BasicComponent(B);
    }

    BasicComponent(const int& mB) :B(mB){}
    BasicComponent() :B(0) {}

};

/*************************** DERIVED CLASS 2 **************************/
class AdvancedComponent: public Component
{
private:
    float A = 0.f;
    int iA = 0;
public:
    AdvancedComponent* Clone() override {
        return new AdvancedComponent(A, iA);
    }

    AdvancedComponent(const float& mA, const int& miA) :A(mA),iA(miA) {}
    AdvancedComponent() :A(0.f),iA(0) {}

};

由于我希望实体向量中的每个 Entity 都是唯一的,也就是说,有自己的矩形和组件,我应该如何创建 class ?

我的问题是,class Entity 应该是什么样子?我应该为此 class 创建单独的 CopyConstructor、Assignment Constructor 和 Destructor 吗?另外,如果我想实现将一个实体复制到另一个实体(深度复制),是否有必要拥有所有 3 个(复制、赋值和析构函数)?

My question here is, what should the class Entity look like ? Should I create separate CopyConstructor, Assignment Constructor and Destructor for this class ? Also if I want to implement copying one Entity into another (deep copying), is it necessary to have all 3 (Copy, Assignment and Destructor) ?

这个问题的答案实际上并不取决于 Entity 看起来像什么,而是取决于它打算具有的语义。

你说每个 Entity 都是“唯一的”,但 C++ 中的每个对象(甚至每个 int,即使它们碰巧具有相同的值)在技术上都是唯一的。那你到底是什么意思?

  1. Entity 应该是可复制的吗?复制构造一个 Entity 意味着两个 Entity 对象具有相同的内容(如果组件指针是浅复制,则字面意思是,如果是深复制,则逻辑上是相同的)。

    如果不是,您可能不想编写(或者可能显式 delete )复制构造函数 and/or 复制赋值运算符。

  2. Entity 应该是可移动的吗?可能是的,因为它不违反唯一性并使它们更容易有效地使用。

    如果是这样,您应该确保它具有移动构造函数和移动赋值运算符(通过编写它或安排编译器生成有用的默认值)。

Also if I want to implement copying one Entity into another (deep copying)

这似乎违反了您的唯一性约束,但是是的,您需要一个复制构造函数和复制赋值运算符。

但是,最佳做法是避免将资源管理与程序逻辑交织在一起。因此,与其编写所有这些,不如考虑让智能指针为您自动完成。请参阅 零规则 以进行比较 this answer.

事实上,我们可以用很少的代码说明所有合理的语义:

template <typename ComponentPtr>
struct ZeroEntity
{
  Rectangle bound;
  std::vector<ComponentPtr> components;
};

using ShallowCopyZeroEntity = ZeroEntity<std::shared_ptr<Component>>;
using NoCopyOnlyMoveEntity = ZeroEntity<std::unique_ptr<Component>>;
using DeepCopyZeroEntity = ZeroEntity<my::clone_ptr<Component>>;

除了我们还需要写一个深拷贝clone_ptr,比如

namespace my {
template <typename T>
class clone_ptr
{
  std::unique_ptr<T> p_;

  std::unique_ptr<T> clone() const { return std::unique_ptr<T>{p_ ? p_->Clone() : nullptr}; }

public:

  using pointer = typename std::unique_ptr<T>::pointer;

  explicit clone_ptr(pointer p) : p_(p) {}

  // copy behaviour is where the cloning happens
  clone_ptr(clone_ptr const& other) : p_(other.clone()) {}
  clone_ptr& operator=(clone_ptr other)
  {
    other.swap(*this);
  }

  // move behaviour (and destructor) generated by unique_ptr
  clone_ptr(clone_ptr&& other) = default;
  clone_ptr& operator=(clone_ptr&&) = default;

  // now write all the same swap, release, reset, operator* etc. as std::unique_ptr
};
}

... 如果这看起来很多,想象一下它与您的 Entity 代码交错而不是像这样收集到一个地方会多么混乱。

主要目的只是为了启用实体的复制。这个有用吗?

#include "Components.h"
#include "Rectangle.h"
#include <vector>
using namespace std;
class Entity
{

public:
    Rectangle& Box;
    vector<Component*>& Components;

    //Default constructor call
    Entity(): Box(*new Rectangle), Components(*new vector<Component*>){}
 
    //Constructor call with parameters
    Entity(Rectangle& B, vector<Component*>& C) : Box(B), Components(C){}

    //Copy constructor that makes Entity copyable
    Entity(const Entity& E) : Box(*new Rectangle(E.Box)), Components(*new vector<Component*>(E.Components.size())) {
     
 
        for (unsigned int i = 0; i < E.Components.size(); i++) {
            Components[i] = E.Components[i]->Clone();
        }
    }

    //Assignment operator constructor
    Entity& operator=(const Entity& E){ 
        //Entity* pE = &E;
        if (this != &E)
        {
            delete& Box;
            delete& Components;
            Box = *new Rectangle(E.Box);
            Components = *new vector<Component*>(E.Components.size());
            for (unsigned int i = 0; i < E.Components.size(); i++) {
                Components[i] = E.Components[i]->Clone();
            }
        }
         return *this;

    }

};