您的位置:首页 > 编程语言

第四章 基于对象的编程风格何谓mutable(可变)和const(不可变)

2015-04-15 20:37 381 查看
4.3何谓mutable(可变)和const(不变)

看看下面的函数:

int sum(const Triangular &trian)

{

int beg_pos=trian.beg_pos();

int length=train.length();

int sum=0;

for(int ix=0;ix<length;++ix)

sum+=train.elem(beg_pos+ix);

return sum;

}

train是个const reference参数,因此编译器必须保证train在sum()之中不会被修改。但是,sum()所调用的任何一个member fuction都有可能更改train的值。为了确保train不被修改,编译器必须保证beg_pos(),elem()都不会更改调用者。编译器如何得知这项信息呢?是的,class设计者必须在member function身上标注const,依次来告诉编译器:这个member function不会更改class object的内容:

class triangular

{

public://以下是const member function

int length(); const{return _length;}

int beg_pos();const{return _beg_pos;}

int elem(int pos);const;

//以下是non const member function

bool next(int &val);

void next_reset(){_next=_beg_pos-1}

//....

private:

int_length;//元素个数

int_beg_pos;//起始位置

int_next;//下一个迭代目标

//static data member将于4.5节说明

static vector<int>_elems;

};

const修饰符紧接于函数参数列表之后。凡是在class主体以外定义者,如果它是一个const member function,那就必须同时在声明与定义中指定const。

例如:

int triangular::elem(int pos)const

{return _elem[pos-1];}

虽然编译器不会为每个函数进行分析,决定它究竟是const还是non-const,但它会检查每个声明为const的member function,看它们是否真的没有改变class object的内容。例如,假如我们将以下的next()声明为const member function,就会产生编译错误,因为很显然它会更改其他调用者的值。

bool triangular::next(int&value)const

{

if(_next<_beg_pos+_length-1)

{

//错误,更改了_next的值

value=_elems[next++];

return false;

}

下面这个class,val()并不直接修改_val,但它却会返回一个non-const reference指向_val.那么,val()可被声明为const吗?

class val_class

{

public:

val_class(const bigclass&v)

:_val(v){}

//这样没有问题么?

bigclass& val() const {return _val;}

private:

bigclass_val;

};

不,这会产生问题(但语法层次正确),返回一个non_const refenrence指向_val.

那么,val()可被声明为一个const吗?

class val_class

{

public:

val_class(const Bigclass &v)

:_val(v)

{}

//这样没有问题么

Bigclass& val() const

{

return _val;

}

private:

Bigclass _val;

};

不,这会产生问题,返回一个non_const reference指向val.

实际上等于将_val开放出去,允许程序在其他地方加以修改。

由于member function可以根据const与否而重载。因此有个方法可以解决这个问题:提供两份定义,一为const版本,二为non-const版本。例如:

class val_class

{

public:

const Bigclass& val() const{return _val;}

Bigclass& val()

{return _val;}

//.....

};

non-const class object会调用non-const版的val()(于是对象内容改变也没有关系),const class object则会调用const版本的val()(那就不可能改变对象的内容)。这样就完全没有问题呢。举个例子:

void example(const Bigclass *pbc,Bigclass &rbc)

{

pbc->val();//这会调用const版本

rbc.val();//这会调用non-const版本

};

设计class时,鉴定其const member function是一件很重要的事情。如果你忘了这么做,要知道,没有一个const reference class的参数可以调动公开接口中的non-const成分(但目前有许多编译器对此情况都只给警告)。用户也许会因此大声咒骂。将const加到class内并非易事。特别是如果某个member function被广泛运用之后。

Mutable data member(可变的数据成员)

以下是sum()的另一种做法,借由next()和next_reset()两个member function对于train元素进行迭代:

int sum(const Triangular &train)

{

if(!train.length())

return 0;

int val,sum=0;

train.next_reset();//把下一个成员排序

while(train.next(val))

sum+=val;

return sum;

}

这段程序会通过编译吗?不,至少现在不行。train是个const object,而next_reset()和next()都会更改——next()值,它们都不是const member function.但是它们却被train调用,于是造成错误。

我们很希望采用sum()的这份实现,则next()和next_reset()势必得改为const.但它们真的改变了_next的值!我们可以做一个很好的区别。

检讨一下,_length和_eg_pos提供了数列的抽象属性。如果我们改变train的长度或者起始位置,形同改变其性质,将和未改变前的状态不再相同。然而_next只是让我们得以实现出iterator机制。它本身不属于数列抽象概念的一环。改变_next的值,从意义上讲,不能视为改变class object的状态。或者是说破坏了对象的常量性(constness)关键字mutable可以让我们做出这样的声明。只要将_next标示为mutable,我们就可以宣称:对_next所做的改变并不会破坏class
object的常量性。

class Triangular:

{

public:

bool next(int &val)const;

void next_reset() const {_next=_beg_pos-1:}

//....

private:

mutable int _next;

int _beg_pos;

int _length;

};

现在,next()和next_reset()既可以修改_next的值,又可以被声明为const member function.这么一来,前述的sum()实现便没有问题了。一下便是这三个Triangular object实地演练sum():

int main()

{

Triangular tri(4)

cout<<tri<<"--sum of elements:<<sum(tri)<<endl;

Triangular tri2(4,3);

cout<<tri2<<"--sum of elements:"<<sum(tri2)<<endl;

Triangular tri3(4,8);

cout<<tri3<<"--sum of elements:"<<sum(tri3)<<endl;

}

编译执行,产生如下结果:

(1,4) 1 3 6 10 --sum of elements:20

(3,4)6 10 15 21 --sum of elements:52

(8,4) 36 45 55 66 --sum of elements:202

};

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: