2013-07-21 11 views
2

我有一个类 - 它基本上是一堆字段的结构(比如说,像一个2-D点;但它只是一个综合示例,用于这个问题) - 其中我只想要不可变实例。很显然,我可以使数据成员私有的,只能有getter方法:使用const public成员强制所有实例的不变性是否合理?

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内斯强制不变性,与类本身是可变的,即

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); 

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

+0

不知道,但无论你选择什么,你的方法应该是'const'。 – juanchopanza

+0

@juanchopanza:嗯,当然是的。纠正。 – einpoklum

回答

0

两者都有效。

这两者之间最有可能没有性能差异,因为getX()getY()被优化为“无”。 [你可能想用GetY这个'return y;',它可能有助于解决一个或两个错误]。

这主要是一个风格问题。

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

+0

问题是,这是可以接受的风格。 – einpoklum

+0

取决于你问的问题 - 风格就是这样 - 不是法律。 –

0

我不确定是否可以为这个问题提供一个事实,但在我看来,使用获取和设置像Point2D这样的小对象是多余的。如果你打算使用这些对象作为常量,只需使它们保持不变。如果你打算使用它们作为变量,那么只需重载诸如'+','+ ='等运算符并使用它们。 Point2D作为数据成员应该被封装,而不是Point2D的数据成员。再次,如果您要以不可变的方式使用它们,请使用const Point2D。您可能想稍后更改(以可变方式使用)Point2D的数据。常量数据成员对这个类没有任何用处。

+0

'常量数据成员对这个类没有任何作用。“......它提高了可读性并隐式地删除了赋值运算符。所以它的目的。 '使用get和set2来设置像Point2D这样的小对象是多余的。... ...在这个问题中,很明显提到代码只是表达意图的一个例子,不管类是小还是大。 – iammilind

+0

@iammilind:他/她可能会在其他程序中以可变方式使用Point2D,那么为什么要降低一个类的可用性?我不能说出未知情况,我只能说出我看到的内容,并且在此示例中添加get和set是多余的,降低了可读性。当涉及到决定类是否是不可变的时候,类的目的也很重要。根据我的经验,我从来没有见过一个Point2D(代表一个坐标系)类型的类,它具有常量字段。 –

+0

对你评论中的第一行的回答:在问题中明确提到:' - 我只想要不可变的实例。除此之外,不管怎么说,建议的方法都不是很多人都知道的,这就是它不常见的原因。 – iammilind

1

这是否被认为是合理的?

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

  • const数据==>的构件不会在其寿命
  • public ==>的构件被读取class
  • protected构件==>的部件的外部被改变在继承层次结构
  • private构件==>的构件在class范围仅
内读取内读

所以这里的意图是明确的。只要您确定在其生命周期中不会发生变化,您就立即声明会员const。应该根据潜在的吸气剂方法将被使用的位置来选择访​​问说明符。

而且,我真的需要在与 const public成员的规范方法const修饰?

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

编辑
关于修改的问题。一旦你在class里有一个const成员,operator =就会被隐式删除,所以你不必= delete它。这只是一个品味问题。对我而言,它变得多余。
事实上,在G ++编译器,如果您尝试使用分配修改这样的对象,编译器会通过自身产生的错误,如:

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

其他语句是一个函数声明:

const Point2d findMagicPoint(); 

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

+0

关于'norm()''const'-modifier - 为什么编译器不会推断它,因为所有的数据成员都是常量?是因为继承吗? – einpoklum

+1

@einpoklum,这是因为'const_cast <>'功能在C++中可用:)。请注意,编译器只是在头文件中查看函数的声明以决定它是否是'const'正确的。身体可以躺在任何地方,编译器可能不够聪明,以至于数据在身体内部仍然是不变的。这将是太多的分析。所以最好写一个'const'版本。 – iammilind

+0

将再次修改第三个代码示例,使其不仅仅是一个右值。 – einpoklum

2

在灵活性和保证之间设计一个班级时总会有一种紧张感。强化数据成员的一致性(这排除了分配),使得一个非常严格的类:这有一个成本(失去灵活性),但是收益是多少?

当然,任何希望冻结实例的用户都可以使用const这样做。所以你没有给用户带来任何“新”功能,但是你剥夺了他们的灵活性:这是否值得?

我想在这里你太过分了,因为这个任务不起作用,你可能会发现自己在最常见的操作中使用类的实例时遇到困难(你可以创建它们的一个数组,但不能在以后排序该阵列)。

因此,我建议你只是防止任何修改分配。这足以毫不费力地维护类不变量。

相关问题