使用const公共成员强制所有实例的不变性是合理的做法吗
Is it reasonable practice to enforce the immutability of all instances using const public members?
我有一个类,它基本上是一堆字段的结构(比如说,类似于二维点的东西;但它只是这个问题的合成示例),我只想要不可变的实例。显然,我可以使数据成员私有,并且只有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);
加强不变性的更合理的方法是什么?
在设计一个类时,灵活性和保证之间总是存在张力。强制执行数据成员的常量(排除了赋值)会产生一个非常严格的类:这有成本(灵活性的损失),但收益是什么?
当然,任何希望冻结实例的用户都可以使用const
来执行此操作。所以你没有给用户带来任何"新"功能,但你剥夺了他们的灵活性:这值得吗?
我认为你太极端了,因为赋值不起作用,你可能会发现自己在最常见的操作中使用类的实例时遇到了困难(你可以制作一个数组,但以后不能对数组进行排序)。
因此,我建议您简单地防止在分配之外对进行任何修改。这足以毫不费力地维护类不变量。
这是否合理?
是的。在我看来,这是一种更好的风格,因为它提高了可读性
总有一天,比起getter方法,我更喜欢const
(不可变)public
成员。实际上,如果以正确的方式声明,每个数据类型都会发送特定的消息。例如:
const
数据===>成员的寿命public
=>在class
之外读取成员protected
成员===>在继承中读取该成员世袭制private
成员===>该成员仅在class
范围内读取
所以这里的意图是明确的。一旦确定成员const
在其生存期内不会更改,就可以声明该成员。应该根据潜在getter方法的使用位置来选择访问说明符。
此外,我真的需要使用
const public
成员?
是的。这是最好的,因为您需要const
对象或其他const
方法中的const
正确版本。
编辑:
关于修改后的问题。一旦你在class
中有一个const
成员,operator =
就会被隐式删除,所以你不必= delete
。这只是品味的问题。对我来说,它变得多余了
事实上,在g++编译器中,如果你试图使用赋值修改这样的对象,编译器会自己生成一个错误,比如:
error: use of deleted function ‘Point2D& Point2D::operator=(const Point2D&)’
另一条语句是函数声明:
const Point2d findMagicPoint();
返回类型不需要是const
,因为它无论如何都是不可修改的右值。例如CCD_ 21是不成形的。
两者都能工作。
两者之间很可能没有性能差异,因为getX()
和getY()
被优化为"零"。[您可能想在GetY
tho'中使用return y;
,这可能有助于解决一两个错误]。
这主要是一个风格问题。
当然,你可能也会有这个类的non-const
版本,当你想在使用它之前对点进行计算时。
我不确定是否可以为这个问题提供一个事实,但在我看来,对像Point2D这样的小对象使用get和set是多余的。如果要将这些对象用作常量,只需将它们设为常量即可。如果要将它们用作变量,那么只需重载运算符(如"+"、"+="等)并使用它们。应该封装作为数据成员的Point2D,而不是Point2D的数据成员。同样,如果要以不可变的方式使用它们,只需使用const Point2D
即可。以后您可能需要更改(以可变方式使用)Point2D的数据。常量数据成员对此类没有任何作用。
- 未定义的类模板不会实例化以检查友元函数
- 如何在不创建实例的情况下获取类的方法成员的类型?
- 如何编写模板重载函数,并在模板参数不允许实例化某个类时触发回退
- Clang和Gcc不同意实例化后的显式专业化
- 为什么不可能实例化原子对
- C++,不能实例化抽象类
- 为什么我不能实例化其构造函数在友元类中私有的类
- 编译的程序是否有可能不包含实例化的模板类
- 如何在不引起实例化的情况下获取功能模板的签名
- 在不指定实例化的情况下调用类模板的静态方法的方法
- 当丢失非const虚拟方法时,为什么我不能实例化类的const实例
- 在不构造实例变量的情况下C++声明实例变量的好方法是什么?
- 为什么不能在不创建实例的情况下使用多态类
- 访问不带实例化对象的非静态成员函数
- C++中右值引用的分配和不变性
- 关于 Python 元组的不变性
- C++ 类的不变性和优缺点
- 为什么不总是实例化堆栈上的对象?C++
- 不能实例化抽象类的对象,但必须返回该类的对象
- 使用const公共成员强制所有实例的不变性是合理的做法吗