第四章 基于对象的编程风格何谓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
};
}
看看下面的函数:
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
};
}
相关文章推荐
- 第四章 基于对象的编程风格(什么是this指针)
- 第四章 基于对象的编程风格(打造一个iterator class)
- 第四章 基于对象的编程风格(什么是构造函数和析构函数)
- 第四章 基于对象的编程风格(如何实现一个class)
- 面向对象编程风格&基于对象编程风格
- Essential C++中文版(Chap4:基于对象的编程风格)
- C++服务器开发之基于对象的编程风格
- muduo网络库学习笔记(0):面向对象编程风格和基于对象编程风格的比较
- 09基于对象编程风格
- muduo2 面向对象编程风格 VS 基于对象编程风格(boost::bind/function)
- 《Essential C++》笔记五、基于对象的编程风格
- 面向对象编程风格 VS 基于对象编程风格(boost::bind/function)
- Essential C++笔记(基于对象的编程风格)
- C++服务器开发之基于对象的编程风格
- 面向对象编程风格 & 基于对象编程(boost::bind/function)
- c++基于对象的编程风格2
- C++服务器开发之基于对象的编程风格
- 关于Python参数传递时,传递可变对象(mutable)和不可变更对象(immutable)的误区
- Spark RDD编程(Python和Scala版本)----Spark中的RDD就是一个不可变的分布式对象集合,是一种具有兼容性的基于内存的集群计算抽象方法,Spark则是这个方法的抽象。 Spa
- 面向对象编程风格 VS 基于对象编程风格(boost::bind/function)