想象一下,我有一个类用于表示一些琐碎的数值数据,比如矢量。我希望这个类在其成员方面是不可变的,但仍然支持正确的拷贝分配行为。
这个类的shell可能是这样的:
class Vector {
public:
Vector(float _x, float _y);
private:
const float x;
const float y;
};
我希望能够在(例如)类中为向量成员分配新值。具有位置的屏幕实体,可能使用类型为Vector
的非const成员表示。
这个“实体”本身必须是可变的(可能支持运动),例如用于存储其位置的内部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
案例是我发明的事情,可以解释我的观点,我可以想到在会员级别应该是不可变的多种类型。
我是否在浪费时间尝试为不可变类型建模?
有更好的解决方案吗?
答案 0 :(得分:3)
键入
时Vector position;
在类定义中,您定义了一个永远留在这里的Vector
类的实例。如果那个是不可改变的,你就搞砸了。
在C#/ Java中,没有办法做到这一点。而是定义引用。
如果你想在C ++中做同样的事情,你需要指针或auto_pointers
Vector* position;
这样,您可以更改position
指向的实例。
您不应该定义不可变类的赋值运算符。如果你真的需要,也许你应该Vector
可变,并在需要不可变const Vector
时使用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;
}
};
答案 1 :(得分:2)
您尝试做的事与使用operator =无关。原因是你需要一个Vector类的实例才能使用operator =,但你已经声明你不想允许更改内部。
通常情况下,您的运营商=会喜欢以下内容:
Vector::operator=(const Vector &rhs)
{
x = rhs.x;
y = rhs.y;
}
*注意编译器会自动为你生成这个,因为你没有指针等就安全了。
此operator = overload不适用于您的场景,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);
答案 2 :(得分:2)
您的要求存在逻辑上的不一致。 一方面,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;
};
初始化后,无法修改此类的实例,除非使用新实例完全覆盖它们。
答案 3 :(得分:1)
如果您在这里关注三级规则,请创建类noncopyable
,或将复制赋值运算符声明为private
(并保持未定义)。
如果这不适合您的数据类型,那么这表明这些数据成员不应该是const
。
答案 4 :(得分:0)
我认为你在某种程度上误解了const的正确性。如果你真的想要一个不可变的结构,那么拥有一个operator =是没有意义的。
但是,我不明白为什么下面的代码不符合您的需求:
struct Vector
{
Vector(float, float);
Vector(Vector const&);
private:
const float x, y;
// Disable automatic generation of operator=
Vector& operator=(Vector const&);
};
答案 5 :(得分:0)
这应该这样做。
Vector& Vector::operator=(const Vector& _rhs) {
Vector tmp( _rhs.x, _rhs.y );
std::swap( *this, tmp );
return *this;
}