注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

为着理想勇敢前进

 
 
 

日志

 
 

D语言2.0的const和immutable  

2009-08-12 09:45:50|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
翻译后记 2007年的时候,我关注过D语言,甚至还用它来写过一个东西。当时D社区里面讨论得比较激烈的特性就是final/const/invariant这三种数据类型。那时候我就觉得D 2.0的这三个东西会很有用。
过了两年,突然发现这些东西已经改得面目全非,invariant已经改名叫做immutable了,而final数据类型竟然被砍掉了。我在D的论坛上泡了半天也没搞清楚前因后果。所以我翻译了这篇文章,可能会对被D语言这些常量所迷惑的同学有帮助。原文地址:http://www.digitalmars.com/d/2.0/const3.html

对数据结构或接口来说,如果能轻易地分辨出哪些数据不会改变,哪些数据可能会改变,以及谁可能改变这些数据,就会非常有用。以上可借助语言的类型系统做到。数据可以被标记为const或immutable,而其默认是可变的(即mutable ) 。

不 可改变的数据可以用immutable表示。immutable数据构建之后,在程序整个运行期间都会保持原值。immutable数据可以放在 ROM(只读存储器)中或者由硬件设为只读的内存中。由于immutable数据不会改变,所以可以得到许多程序优化的机会,还可便于采用函数式编程。

const适用于不能通过引用本身改变数据的引用。不过,这块数据还时可能被其他引用所改变。若向接口传入数据时需要保证该接口中不修改该数据,则应采用const。

immutable和const都是transtive(具有可传递性),这意味着通过immutable引用取得的数据同样也是immutable的,const亦是如此。

immutable存储类

immutable最简单的用法是用作存储类。可以用它来声明明确的常量。

immutable int x = 3; // 将x设为3
x = 4; // 错误,x不可改变
char[x] s; // s 是一个包含三个char的数组

数据类型也可以由初始化表达式推断:

immutable y = 4; // y的类型是int
y = 5; // 错误,y不可改变

如果不写初始化表达式,相应的构造函数也可以初始化immutable数据:

immutable int z;
void test()
{
z = 3; // 错误,z不可改变
}
static this()
{
z = 3; // 正确,没有静态初始化表达式的immutable数据可以赋值
}

除了函数类的immutable数据外,其他immutable声明的初始化表达式都必须能在编译时求值:

int foo(int f) { return f * 3; }
int i = 5;
immutable x = 3 * 4; // 正确,12
immutable y = i + 1; // 错误,无法在编译时求值
immutable z = foo(2) + 1; // 正确,foo(2)能在编译时求得值——7

非静态的局部immutable数据的初始化表达式是在运行时计算的:

int foo(int f)
{
immutable x = f + 1; // 运行时求值
x = 3; // 错误,x不可改变
}

由于immutable数据具有可传递性,由immutable数据引用的数据也是不变的:

immutable char[] s = "foo";
s[0] = 'a'; // 错误,s指向一块不可改变的数据
s = "bar"; // 错误,s不可改变

immutable声明可以作为左值出现,比如它们的地址都是可以获取到的,再如它们会占据存储空间。

const存储类

除了以下区别之外,以const声明的数据和immutable的完全相同:

  • 不能通过const声明来修改它所引用的数据,但这块数据还是可能被指向该数据的其他引用所修改。
  • const声明自身也是const的。

immutable类型

一定不会被改变的数据可以标记为immutable。immutable关键字可以被用作type constructor:

immutable(char)[] s = "hello";

括号中的类型被标记为immutable。因此,一方面,s能被设为新的值,另一方面,s[]的内容却不能被改变:

s[0] = 'b';  // 错误,s[]不可改变
s = null; // 正确,s自身并不是immutable的

immutable具有可传递性,这意味着被immutable类型引用的数据也是immutable的:

immutable(char*)** p = ...;
p = ...; // 正确,p可以改变
*p = ...; // 正确,*p可以改变
**p = ...; // 错误,**p不可改变
***p = ...; // 错误,***p不可改变

把immutable用作存储类相当于把immutable用作整个声明类型的type constructor:

immutable int x = 3;   // x的类型是immutable(int)
immutable(int) y = 3; // y不可改变

创建不变数据

第一种方式:使用本来就不变的字面量(literal),比如字符串字面量。字符串字面量始终都是immutable的。

auto s = "hello";   // s的类型是immutable(char)[5]
char[] p = "world"; // 错误,immutable类型不能隐式转换为非immutable类型。

第二种方式是把数据转换为immutable。这样做时,需要由程序员确保不存在其他的对该块数据的可变引用。

char[] s = ...;
immutable(char)[] p = cast(immutable)s; // 未定义行为
immutable(char)[] p = cast(immutable)s.dup; // 正确,只存在一个引用

使用.idup属性,可以便利的创建某个数组的不变副本。

auto p = s.idup;
p[0] = ...; // 错误,p[]不可改变

通过转换移除immutable

immutable类型可通过强制转换而被移除。

immutable int* p = ...;
int* q = cast(int*)p;

不过,这并不意味着可以修改这块数据。

*q = 3; // 编译通过,但结果会导致未定义行为

在有些情况下,强制抹除不变性是必要的。比如涉及某个不能更改的库,而库中的静态类型并不正确。有史以来,强制转换一直很直接很有效。使用强制转换抹除不变性时,必须要承担保证数据不变的责任,因为它已经不再由编译器静态保证了。

immutable成员函数

immutable成员函数表示this以及被this引用的任何数据都是不变的。可以这样声明:

struct S
{ int x;

immutable void foo()
{
x = 4; // 错误,x不可改变
this.x = 4; // 错误,x不可改变
}

函数的constimmutable属性也可以写在参数列表的右括号之后:

struct S
{
void bar() immutable
{
}
}

const类型

const类型和immutable类型类似,唯一的区别在于const建立的是数据的只读视图。数据本身随时可能被其他引用了该数据的变量所修改。

const成员函数

在const成员函数中,不允许通过该函数的this指针改变对象任何部分的数据。

隐式转换

immutable和非immutable类型都可以隐式转换为const类型。非immutable类型不能隐式转换为immutable类型,反之亦然。

对比D和C++的immutable/const

Const, Immutable Comparison
特性 D C++98
const关键字
immutable关键字
const表示方式 函数式:
//指向指向const int的const指针的指针
const(int*)* p;
后缀:
//指向指向const int的const指针的指针
const int *const *p;
有传递性的const 有:
//指向指向const int的const指针的const指针
const int** p;
**p = 3; // 错误
无:
//指向指向int的指针的const指针
int** const p;
**p = 3; // 通过
强制抹除const 有:
// 指向const int的指针
const(int)* p;
int* q = cast(int*)p; // ok
有:
// 指向const int的指针

const int* p;
int* q = const_cast<int*>p; //ok
强制抹除const后进行修改 无:
// 指向const int的指针
const(int)* p;
int* q = cast(int*)p;
*q = 3; // 未定义行为
有:
// 指向const int的指针
const int* p;
int* q = const_cast<int*>p;
*q = 3; // 通过
以顶级const区分重载的函数 有:
void foo(int x);
void foo(const int x); //正确
无:
void foo(int x);
void foo(const int x); //错误
变量的const别名 有:
void foo(const int* x, int* y)
{
bar(*x); // bar(3)
*y = 4;
bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
有:
void foo(const int* x, int* y)
{
bar(*x); // bar(3)
*y = 4;
bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
变量的immutable别名 无:
void foo(immutable int* x, int* y)
{
bar(*x); // bar(3)
*y = 4; // 未定义行为
bar(*x); // bar(??)
}
...
int i = 3;
foo(cast(immutable)&i, &i);
没有immutable
字符串字面量的类型 immutable(char)[] const char*
将字符串字面量转换为非常量 不允许 允许但不推荐
  评论这张
 
阅读(662)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018