在具有所有 const 数据成员的类中实现复制赋值运算符是否有意义?

发布于 2024-09-18 05:40:59 字数 1152 浏览 7 评论 0原文

想象一下,我有一个类用于表示一些琐碎的数值数据,例如向量。我希望这个类的成员是不可变的,但仍然支持正确的复制分配行为。

这个类的外壳可能是这样的:

class Vector {
  public:
  Vector(float _x, float _y);

  private:
  const float x;
  const float y;
};

我希望能够为(例如)类中的向量成员分配一个新值。具有位置的屏幕上实体,可能使用 Vector 类型的非常量成员表示。

这个“实体”本身必须是可变的(也许支持移动),例如用于存储其位置的内部Vector成员必须改变。我不想直接修改此成员,而是将其替换为一个全新的 Vector 实例,并使用修改后的值进行初始化。

在 C# 等语言中,我只需根据当前的 Vector 实例计算 X 和 Y 的新值,并将用这些值初始化的新 Vector 对象分配给该成员,并丢弃以前的值并让 GC 完成剩下的工作。

// Inside some class (C#/Java type)...
Vector position;
...
// Example function:
void Move(float _dX, float _dY) {
  this.position = new Vector(_dX + position.X, _dY + position.Y);
}

这就是我有限的 C++ 能力发挥作用的地方,因为我不确定如何实现 operator= ,以便动态内存中包含的这种不可变类型的实例不会简单地“消失”并且导致泄漏。

所以,我想,我要问的是:

Vector& operator=(const Vector& _rhs);
...
// In implementation file...
Vector& Vector::operator=(const Vector& _rhs) {
  // WHAT ON EARTH GOES IN HERE?!?!?
}

Vector 案例是我为了表达我的观点而发明的,我可以想到在成员级别应该不可变的多种类型。

我是否在浪费时间尝试对不可变类型进行建模?

有更好的解决方案吗?

Imagine I have a class used to represent some trivial numerical data, like a vector. I want this class to be immutable in terms of its members, but still support correct copy-assignment behaviour.

This is what the shell of this class might look like:

class Vector {
  public:
  Vector(float _x, float _y);

  private:
  const float x;
  const float y;
};

I would like to be able to assign a new value to a vector member within (for example) a class. An on-screen entity with a position, perhaps, represented using a non-const member of type Vector.

This 'entity' must be mutable itself (perhaps supporting movement), e.g. the internal Vector member used to store its position must change. Rather than modifying this member directly, I would rather replace it with an entirely new Vector instance, initialised with the modified values.

In a language such as C#, I would simply compute the new values for X and Y based on the current Vector instance, and assign a new Vector object initialised with these values to the member, discarding the previous value and letting the GC do the rest.

// Inside some class (C#/Java type)...
Vector position;
...
// Example function:
void Move(float _dX, float _dY) {
  this.position = new Vector(_dX + position.X, _dY + position.Y);
}

This is where my limited ability with C++ comes into play, because I am unsure of how to implement operator= in such a way that instances of this immutable type contained in dynamic memory do not simply 'disappear' and cause a leak.

So, I suppose, what I am asking is this:

Vector& operator=(const Vector& _rhs);
...
// In implementation file...
Vector& Vector::operator=(const Vector& _rhs) {
  // WHAT ON EARTH GOES IN HERE?!?!?
}

The Vector case is something I invented to get my point across, I can think of many sorts of types that should be immutable at the member level.

Am I wasting my time attempting to model immutable types?

Is there a better solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

违心° 2024-09-25 05:40:59

当您输入

Vector position;

类定义时,您定义了一个 Vector 类的实例,该实例将永远保留在此处。如果那个是一成不变的,那你就完蛋了。

在C#/Java中,没有办法做到这一点。相反,您定义引用。

如果你想在 C++ 中做同样的事情,你需要指针或 auto_pointers

Vector* position;

这样,你就可以改变 position 指向的实例。

您不应该定义不可变类的赋值运算符。如果您确实需要,也许您应该使 Vector 可变,并在需要不可变的 Vector 时使用 const Vector。您只需将 const 限定符添加到 const Vector 允许的方法中。

例如:

class Vector {
    private:
    float x;
    float y;
    public:
    // allowed for const Vector
    // because of the const before the '{'
    float norm() const {
        return hypot(x,y);
    }

    Vector& operator=(const Vector& o) {
        x=o.x;
        y=o.y;
        return *this;
    }
};

When you type

Vector position;

in a class definition, you define an instance of a Vector class that will stay here forever. If that one is immutable, you're screwed.

In C#/Java, there is no way to do this. Instead you define references.

If you want to do the same thing in C++, you need pointers or auto_pointers

Vector* position;

That way, you can change the instance to which position is pointing.

You shouldn't define the assignment operator of a immutable class. If you really need to, maybe you should make Vector mutable, and use const Vector when you need an immutable Vector. You will just need to add const qualifiers to method that are allowed with a const Vector.

eg:

class Vector {
    private:
    float x;
    float y;
    public:
    // allowed for const Vector
    // because of the const before the '{'
    float norm() const {
        return hypot(x,y);
    }

    Vector& operator=(const Vector& o) {
        x=o.x;
        y=o.y;
        return *this;
    }
};
小伙你站住 2024-09-25 05:40:59

您尝试做的事情对于与运算符=一起使用没有意义。原因是您需要 Vector 类的实例才能使用运算符=,但您已经声明您不想允许更改内部结构。

通常,你的operator=会喜欢这样的东西:

Vector::operator=(const Vector &rhs)
{
   x = rhs.x;
   y = rhs.y;
}

* 注意编译器会自动为你生成这个,并且它会很安全,因为你没有指针等。

此运算符=重载不适用于您的场景,b/c 数据成员 x 和 y 不会重新分配 b/c 您将它们设置为 const。您可以在此处使用带有初始化列表的复制构造函数。如果你真的想要有operator=,你可以在重载中使用const_cast,但大多数人会认为这种形式很糟糕。

Vector::Vector(const Vector ©)
   :x(copy.x), y(copy.y)
{

}

* 请注意,编译器还会自动为您生成这种类型的复制构造函数。

然后你的代码将创建对象

Vector newObj(oldObj);

已经陈述了所有这些来回答你的问题,为什么不让成员是非 const 并声明 Vector const 呢?

const Vector myVector(5,10);

What you're trying to do doesn't make sense for use with the operator=. Reason being is you need an instance of your Vector class in order to use operator=, but you've stated you do not want to allow changes to the internals.

Normally, your operator= would like something like:

Vector::operator=(const Vector &rhs)
{
   x = rhs.x;
   y = rhs.y;
}

* Note the compiler would automatically generate this for you and it would be safe as you have no pointers,etc.

This operator= overload won't work for your scenario, b/c the data members x and y will not reassign b/c you have them as const. This is where you would use a copy-constructor with an initialization list. If you really wanted to have operator=, you could use const_cast inside the overload, but most would consider this poor form.

Vector::Vector(const Vector ©)
   :x(copy.x), y(copy.y)
{

}

* Note the compiler would also automatically generate this type of copy constructor for you.

Then your code would create the object with

Vector newObj(oldObj);

Having stated all this to answer your question, why not just let the members be non-const and declare the Vector const?

const Vector myVector(5,10);
久随 2024-09-25 05:40:59

您的要求存在逻辑不一致。
一方面,Vector 类的所有对象都必须是不可变的,但另一方面您希望能够更改它们。这两个要求是直接冲突的。

更好的设计可能是使类的更改值的唯一方法是将类的新对象分配给它:

class Vector {
public:
  Vector(float _x, float _y);
  /* Vector(const Vector&) = default; */
  /* Vector& operator=(const Vector&) = default; */

  float x() const;
  float y() const;
  /* No non-const accessors!! */
private:
  float x;
  float y;
};

一旦初始化,此类的实例就无法修改,除非用新实例。

You have a logical inconsistency in your requirements.
On the one hand, all objects of class Vector must be immutable, but on the other hand you want to be able to change them. These two requirements are in direct conflict with each other.

A better design might be to make the classes such that the only way to change the value is to assign a new object of the class to it:

class Vector {
public:
  Vector(float _x, float _y);
  /* Vector(const Vector&) = default; */
  /* Vector& operator=(const Vector&) = default; */

  float x() const;
  float y() const;
  /* No non-const accessors!! */
private:
  float x;
  float y;
};

Once initialised, instances of this class can't be modified, except by completely overwriting them with a new instance.

南城追梦 2024-09-25 05:40:59

如果您担心此处的三条规则,请将类设为不可复制,或将复制赋值运算符声明为私有(并将其保留为未定义)。

如果这不适合您的数据类型,则表明这些数据成员不应该是 const。

If you're concerned about rule of three here, make the class noncopyable, or declare the copy assignment operator as private (and leave it undefined).

If that's not appropriate for your datatype, then that is an indication that those data members should not be const.

变身佩奇 2024-09-25 05:40:59

我认为你有点误解了 const 的正确性。如果你真的想要一个不可变的结构,那么使用operator=是没有意义的。

但是,我不明白为什么下面的代码不符合您的需求:

struct Vector
{
    Vector(float, float);
    Vector(Vector const&);

private:
    const float x, y;

    // Disable automatic generation of operator=
    Vector& operator=(Vector const&);
};

I think you are misunderstanding const correctness somewhat. If you really want an immutable structure, having an operator= does not make sense.

However, I fail to see why the code below doesn't fit your needs:

struct Vector
{
    Vector(float, float);
    Vector(Vector const&);

private:
    const float x, y;

    // Disable automatic generation of operator=
    Vector& operator=(Vector const&);
};
笑咖 2024-09-25 05:40:59

这应该可以做到。

Vector& Vector::operator=(const Vector& _rhs) {
    Vector tmp( _rhs.x, _rhs.y );
    std::swap( *this, tmp );
    return *this;
}

This should do it.

Vector& Vector::operator=(const Vector& _rhs) {
    Vector tmp( _rhs.x, _rhs.y );
    std::swap( *this, tmp );
    return *this;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文