不可变与const之间的区别


28

我经常看到这些术语,immutable并且const可以互换使用。但是,从我的(很少)经验来看,两者在代码中的“合同”上有很大不同:

不可变,使合同,这个对象就不会改变,任何(如Python的元组,Java字符串)。

Const使合约在此变量的范围内不被修改(不保证在此期间,其他线程可能会对指向的对象执行任何操作,例如C / C ++关键字)。

显然,除非语言是单线程(PHP)或具有线性或唯一性键入系统(Clean,Mercury,ATS),否则两者并不等效。

首先,我对这两个概念的理解正确吗?

第二,如果有区别,为什么它们几乎只能互换使用?


1
const并非每种语言都存在,并且并非每种语言都存在可变性和不变性,因此使这种语言成为对立语言并不适用。仅在这些概念适用的地方特定于语言。

2
相关的推荐读物:不变性的种类(一些C#示例,但在很大程度上与语言无关)。有人给埃里克·利珀特(Eric Lippert)一枚勋章。

Answers:


14

我将与C ++谈一谈,这种区别最相关。

如您所正确指出的,不可变意味着对象在创建后根本无法更改。这种创建当然可以在运行时发生,即const对象不一定是编译时常量。在C ++中,如果满足(1)和(2)或(3),则对象是不可变的:

  1. 它没有声明mutableconst成员函数突变的成员

  2. 宣布 const

  3. const成员函数不用于const_cast删除const资格以突变任何成员

但是,您也可以考虑使用访问修饰符:如果某个操作在内部对实例进行了更改,但对通过其公共接口可观察到的实例状态没有影响,则该对象是“逻辑上不可变的”。

因此,C ++提供了创建不可变对象所必需的工具,但是像C ++中的大多数其他工具一样,这些工具仅是最低限度的需求,并且需要勤勉地实际使用。实例的状态不一定限于实例成员变量,因为C ++没有提供强制引用透明的方法,它还可以包括全局或类状态。

const在C ++中还具有另一个功能:限定引用和指针。甲const参考可以指非const对象。当且仅当该对象被声明为非-时,使用const_cast该对象来通过const引用对其进行突变是合法的(尽管通常不是必需的或不建议的):const

int        i = 4;         // Non-const object.
const int* p = &i;        // const pointer.

*const_cast<int*>(p) = 5; // Legal.

当然,改变const对象是不确定的行为:

const int  i = 4;         // const object.
const int* p = &i;        // const pointer.

*const_cast<int*>(p) = 5; // Illegal.

19

对于Java,其中关键字“ final”代表“ const”,请考虑:

final Person someone = new Person();

这意味着someone永远不能引用另一个Person对象。但是,您仍然可以更改被推荐人的详细信息。例如someone.setMonthlySalary(10000);

但是,如果someone是“ Immutable”对象,则满足以下条件之一:(a)您将没有名为setMonthlySalary (b)的方法。调用setMonthlySalary始终会引发异常,例如UnsupportedOperationException


10

不可变对象是创建后不会改变状态的对象。例如;

string prefix = "Pre";
string postfix = "Post";
string myComplexStr = prefix + postfix;

在此示例中,myComplexStr对象是不可变的,但不是恒定的,因为它的值是经过计算的。它是不可变的,因为它是一个字符串,具有静态长度属性,并且不能更改。

Const对象通常用于标识一些实常数,例如Pi,“ USA”,“ StackOverflow.com”,端口号等,它们的值在编译前就已知。

从这个角度来看,Const与不可变对象不同,因为它们的值不是由程序计算的。

但是,如果您在谈论C ++中的“ const”关键字,则可以说“ const”用于创建不可变对象。


1
C ++中的const不会创建不可变的对象,它只是一个访问级别。
克莱姆(Klaim)2012年

您能解释一下“ const double pi = 3.14”是如何不可变的吗?
Mert Akcakaya

好吧,这取决于它在哪里。可以说我做了:“ double * p_pi = const_cast <double *>(&pi); * p_pi = 42;” 例如。然后,如果pi在全局空间或名称空间中,则会出现分段错误,但我相信这是未定义的行为,而不是特定的错误。如果pi是运行时可用的任何对象的成员,而这些对象不是静态的,我得到pi ==42。您会看到,即使使用mutable也是可用的,因为C ++中的const是关于访问级别,语义而非数据不变性的,用C ++几乎不可能实现。您只能“模拟”它。const不是一成不变的。
克莱姆(Klaim)2012年

1
@Klaim突变一个const类似的行为是不确定的行为,无论它在哪里分配,IIRC。而且未定义的行为比保证发生的任何特定错误还要糟糕。这意味着您不再使用C ++-C ++无法提供更改const值的方法(mutable当然,除了成员之外,但这不是您的意思),因此就C ++而言,您不能这样做。特定实现的实现完全是另一回事了(我敢打赌,如果您使用优化进行编译,则所使用的特技不会影响以后使用的表达式,pi因为它已被替换)。

“ C ++中的const不会创建不可变的对象”仍然是错误的,因为它仍然会创建全局常量,如您在自己的答案中所述。当然,关键字在某种程度上是语义的,否则,如果您甚至渴望使用未定义的行为,则始终可以手动更改存储单元的电压并更改不可变对象的值。
Mert Akcakaya

8

首先,我对这两个概念的理解正确吗?

是的,但是第二个问题表明您不了解这些差异。

第二,如果有区别,为什么它们几乎只能互换使用?

const在C ++中,仅用于访问级别(表示“只读”),而不用于不变性。这意味着访问本身与数据完全分开。例如,您可以操纵一些数据,然后通过const引用将其公开。访问是只读的,但是数据本身是可变的,因为所有datam都是可变的。

const仅保证访问权限的限制,而不变性(例如在D中)实际上意味着没有办法在对象生命的任何阶段更改数据

现在,您可以通过确保不能以const以外的任何其他方式访问某些数据,并确保已对其进行初始化然后不再对其进行操作,来模拟C ++中的不变性。但这并不是一个有力的保证,因为当您将数据标记为不可变时,像D这样的语言会为您提供。该语言确保根本不可能进行任何修改该数据的操作,而在C ++中,如果确实需要,您仍然可以通过const强制转换和可变性来更改数据。

最后,它根本不一样,因为根本不提供相同的保证。


3

说到JavaScript,关键字constObject.freeze

const适用于绑定variables。它创建了一个不可变的绑定,您不能为其分配新的值。

Object.freeze处理对象值。它使对象不可变。即,您不能更改其属性。


0

在C ++中,它们是相同的。尽管可以更改const对象(如果在内存中具有对象的位置,并且具有写入该内存的操作系统权限)。


1
实际上,这是反对它们相同的一个论点:C ++根本就没有不可变的关键字或语言支持。而且,我假设程序员以一种理智的方式利用该语言:否则,const也绝对没有任何价值。
K.Steff

1
@ K.Steff -也许不如说没有多余的不可变的烦躁在C ++以外的常量提供
马丁贝克特

绝对精确:)
K.Steff

在C ++中,它们根本不一样。const是“只读”访问级别,并不意味着数据是不可变的。您通常可以在C ++中绕过它。
克莱姆(Klaim)2012年

0

在C,C ++和相关语言中,对象为const与引用或指向该对象的指针为常量引用之间也存在差异。

如果尝试修改常量对象,则会得到不确定的行为。(您可以尝试修改常量对象,例如,通过获取其地址,将地址转换为非const指针,然后使用该非const指针来修改该对象)。

另一方面,常量指针或引用仅告诉编译器您不能使用此指针或引用来修改对象。您可以强制转换指针或引用,然后尝试修改对象。如果对象本身是常量,则将发生不良情况。如果对象实际上不是常数,它将改变。当然,这可能会使您的代码用户感到困惑,并且很可能导致错误。

在C语言中,如果使用字符串文字(如“ Hello”),则五个字符和结尾的零字节实际上是常量,但是会得到一个非常量指针。使用该非const指针更改对象是非常糟糕的主意。

在C语言中,可以有一个“常量限制”指针。这意味着指向的对象是暂时不变的。如果在“常量限制”指针处于作用域内的情况下以任何方式修改了对象,则将获得未定义的行为。这比const指针强,后者仅阻止您通过此指针更改对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.