2

我有一个类——它基本上是一堆字段的结构(比如说,像一个二维点;但它只是这个问题的一个综合示例)——我只想要不可变的实例。显然,我可以将数据成员设为私有,并且只有 getter 方法:

class Point2d {
protected:
   double x;
   double y;

public:
   Point2d(double _x, double _y) : x(_x), y(_y) {}
   double getX() const { return x; }
   double getY() const { return y; }
   double norm() const { return x*x + y*y; }
}

但是,我在想也许这样做更好:

class Point2d {
public:
   const double x;
   const double y;

public:
   Point2d(double _x, double _y) : x(_x), y(_y) {}
   double norm() const { return x*x + y*y; }
   Point2d operator=(const Point2d& p) = delete;
}

编辑:第三种选择是const在单个变量的声明中仅使用 ness 来强制不变性,类本身是可变的,即

class Point2d {
public:
   double x;
   double y;

public:
   Point2d(double _x, double _y) : x(_x), y(_y) {}
   double norm() const { return x*x + y*y; }
}

// ...

Point2d magicalTransformation(const Point2d& p1, const Point2d& p2);

执行不变性的更合理方法是什么?

4

4 回答 4

2

在设计灵活性和保证之间的类时,总是存在一种张力。强制数据成员的 const-ness 排除了赋值,导致了一个非常严格的类:这有成本(失去灵活性),但收益是什么?

当然,任何希望冻结实例的用户都可以使用const. 因此,您并没有为用户带来任何“新”功能,而是剥夺了他们的灵活性:值得吗?

我认为您在这里过于极端,因为分配不起作用,您可能会发现自己在最常见的操作中使用类的实例时遇到困难(您可以将它们组成一个数组,但以后不能对数组进行排序)。

因此,我建议您简单地防止分配之外的任何修改。这足以轻松维护类不变量。

于 2013-07-21T11:58:14.623 回答
1

这被认为合理吗?

是的。在我看来,这是一种更好的风格,因为它提高了可读性。
我会更喜欢const(不可变的)public成员而不是吸气剂方法。实际上,如果以正确的方式声明,每种数据类型都会发送特定的消息。例如:

  • const数据 ==> 成员在其生命周期内不会更改
  • public==> 成员在外部读取class
  • protectedmember ==> 在继承层次结构中读取成员
  • privatemember ==> 成员class仅在范围内读取

所以这里的意图很明确。const一旦确定成员在其生命周期内不会被更改,就立即声明它。访问说明符应根据潜在的 getter 方法的使用位置来选择。

另外,我真的需要 const public成员的 norm 方法上的 const 修饰符吗?

是的。这是可取的,因为您需要对象或其他方法内部的const正确版本。constconst

编辑
关于修改后的问题。一旦您在 中拥有一个const成员classoperator =就会隐式删除 ,因此您不必这样做= delete。这只是口味问题。对我来说,它变得多余了。
事实上,在 g++ 编译器中,如果你尝试使用赋值来修改这样的对象,编译器会自己产生错误,例如:

error: use of deleted function ‘Point2D& Point2D::operator=(const Point2D&)’

另一条语句是函数声明:

const Point2d findMagicPoint();

返回类型不需要是const,因为它无论如何都是不可修改的右值。egfindMagicPoint() = <something>;是病态的。

于 2013-07-21T10:30:07.380 回答
0

我不确定是否可以为这个问题提供一个事实,但在我看来,对像 Point2D 这样的小对象使用 get 和 set 是多余的。如果您要将这些对象用作常量,只需将它们设为常量即可。如果您打算将它们用作变量,那么,只需重载诸如“+”、“+=”等运算符并使用它们。Point2D作为数据成员应该被封装,而不是Point2D的数据成员。同样,如果您要以不可变的方式使用它们,只需使用const Point2D. 您可能希望稍后更改(以可变方式使用)Point2D 的数据。常量数据成员对此类没有任何作用。

于 2013-07-21T10:15:31.520 回答
0

两者都会起作用。

两者之间很可能没有性能差异,因为getX()并被getY()优化为“无”。[您可能想使用return y;in GetYtho',它可能有助于解决一两个错误]。

这主要是风格问题。

当然,你可能还会有non-const这个类的一个版本,当你想在使用它之前对点进行计算时。

于 2013-07-21T08:45:42.440 回答