int const *a; file://const修饰指向的对象,a可变,a指向的对象不可变
int *const a; file://const修饰指针a, a不可变,a指向的对象可变
const int *const a; file://指针a和a指向的对象都不可变
(5)修饰常引用
使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:
const double & v;
(6)修饰函数的常参数
const修饰符也可以修饰函数的传递参数,格式如下:
void fun(const int var);
告诉编译器var在函数体中的无法改变,从而防止了使用者的一些无意的或错误的修改。
(7)修饰函数的返回值:
const修饰符也可以修饰函数的返回值,是返回值不可被改变,格式如下:
const int fun1();
const myclass fun2();
(8)修饰类的成员函数:
const修饰符也可以修饰类的成员函数,格式如下:
class classname
{
public:
int fun() const;
.....
};
这样,在调用函数fun时就不能修改类里面的数据
(9)在另一连接文件中引用const常量
extern const int i; file://正确的引用
extern const int j=10; file://错误!常量不可以被再次赋值
另外,还要注意,常量必须初始化!
例如:
const int i=5;
4、几点值得讨论的地方:
(1)const究竟意味着什么?
说了这么多,你认为const意味着什么?一种修饰符?接口抽象?一种新类型?
也许都是,在stroustup最初引入这个关键字时,只是为对象放入rom做出了一种可能,对于const对象,c++既允许对其进行静态初始化,也允许对他进行动态初始化。理想的const对象应该在其构造函数完成之前都是可写的,在析够函数执行开始后也都是可写的,换句话说,const对象具有从构造函数完成到析够函数执行之前的不变性,如果违反了这条规则,结果都是未定义的!虽然我们把const放入rom中,但这并不能够保证const的任何形式的堕落,我们后面会给出具体的办法。无论const对象被放入rom中,还是通过存储保护机制加以保护,都只能保证,对于用户而言这个对象没有改变。换句话说,废料收集器(我们以后会详细讨论,这就一笔带过)或数据库系统对一个const的修改怎没有任何问题。
(2)位元const v.s. 抽象const?
对于关键字const的解释有好几种方式,最常见的就是位元const 和 抽象const。下面我们看一个例子:
class a
{
public:
......
a f(const a& a);
......
};
如果采用抽象const进行解释,那就是f函数不会去改变所引用对象的抽象值,如果采用位元const进行解释,那就成了f函数不会去改变所引用对象的任何位元。
我们可以看到位元解释正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员。
为什么这样呢?因为使用位元const有2个好处:
最大的好处是可以很容易地检测到违反位元const规定的事件:编译器只用去寻找有没有对数据成员的赋值就可以了。另外,如果我们采用了位元const,那么,对于一些比较简单的const对象,我们就可以把它安全的放入rom中,对于一些程序而言,这无疑是一个很重要的优化方式。(关于优化处理,我们到时候专门进行讨论)
当然,位元const也有缺点,要不然,抽象const也就没有产生的必要了。
首先,位元const的抽象性比抽象const的级别更低!实际上,大家都知道,一个库接口的抽象性级别越低,使用这个库就越困难。
其次,使用位元const的库接口会暴露库的一些实现细节,而这往往会带来一些负面效应。所以,在库接口和程序实现细节上,我们都应该采用抽象const。
有时,我们可能希望对const做出一些其它的解释,那么,就要注意了,目前,大多数对const的解释都是类型不安全的,这里我们就不举例子了,你可以自己考虑一下,总之,我们尽量避免对const的重新解释。
(3)放在类内部的常量有什么限制?
看看下面这个例子:
class a
{
private: