前面介绍了const 在常量与指针和const 在函数中的用法,虽然放在不同的位置会有不同的用法,但归根结底, const 的思想都没有变----那就是用来修饰一个不能被改变的量。
然而,const 在类中的含义有所不同。
下面我们来介绍const在类中的具体情况。
const 不仅被用来修饰类的成员,还可以在类外,是整个实例都作为const ,但类中难免有修改数据成员的函数,因此为了保持统一,C++引进了const 成员函数,const 成员函数只能被const 对象调用。
1. 类中的const
有时,我们可能遇到如下情况,创建了一个类,并且希望它的所有实例都有一个统一且确定的值。对于我们来说,最合乎逻辑的方法就是使用const , 但是很遗憾,这样做不会产生预期的效果,虽然const 可以是一个变量常量化,但实际上类中const 修饰的是对象中的成员而非类的。即试剂的作用是为类中的每个对象分配内存,这个对象调用构造函数完成数初始化,一旦完成,就不能再改变。也就是说,对这个const 修饰的成员函数来说,每个不同的对象都可以拥有一个不同的const 值。
const 对象必须在定义时完成初始化。不能先分配内存,过后再赋值,所以在进入构造函数之前,const 数据成员必须已经被赋值。
#include <iostream>
using namespace std ;
class Example{
int x_ ;
const int y_ ;
public :
Example(int x , int y) {
x_ = x ;
y_ = y ; //error
}
//othre functions
};
这样的构造函数是错误的。因为在参数列表(int x , int y)中,系统会先为 x_ 和 y_ 分配单元,等进入函数体之后,才进行赋值。这显然与const的概念不符。const 要求所修饰的对象必须在定义的同时就完成初始化。因此,我们应该使用初始化列表来初始化const 对象,使其在进入函数体之前就已经完成初始化。
#include <iostream>
using namespace std ;
class Example{
int x_ ;
int y_ ;
public :
Example(int x , int y) : y_(y) { //safe
x_ = x ;
}
//othre functions
};
#include <iostream>
using namespace std ;
class Example{
int x_ ;
int y_ ;
public :
Example(int x , int y) : x_(x) , y_(y) {} //also safe
//othre functions
};
2. 编译期间类中的常量
虽说我们解决了const的问题,但之前问题还是没有得到解决——如何在类的每个对象中预设一个固定的值?
答案就是static
static意味着不管这个类的对象被创建多少次,它修饰的对象都只有一个实例,这正是我们需要的类的常量成员。
#include <iostream>
using namespace std ;
class Example{
int x_ ;
int y_ ;
static const int z_ = 100 ;
public :
Example(int x , int y) : x_(x) , y_(y) {}
//othre functions
};
static与其他数据成员区别很大,C++中规定不允许在类内初始化数据成员但static可以,并且,假设我创建了100个Example类的实例,那么每个实例都有一个值为100的const int 。但 z_ 不是他们真正的数据成员,也不和 x_ , y_一样被分配在栈去。而是只被创建一次,并被分配在静态存储区。
3. const对象和成员函数
用户定义的类型和内建类型一样,都可以定义const 对象。
const int i = 1 ;
const Example a(100) ;
都是合法的定义。
我们定义了一个const Example实例a ,a调用构造函数,而之后在该对象的整个生命周期内,都不能修改它的数据成员。
但在实际中如何保证const对象不会调用一个会修改数据成员的成员函数呢?那就是将所有不会修改数据成员的成员函数都声明为const。
比如:
void Display() const {
cout << x_ << endl ;
}
我们应该将所有不会改变数据成员的成员函数都声明为const ,一个没有被声明为const的成员函数将会被看成会改变数据成员的函数。而且编译器不允许非它为一个const成员所调用。
简而言之,一个被const 修饰的成员函数有以下两个特征:
①其中的参数不能被改变,包括不能再调用会修改参数的成员函数。
②const对象和非const对象均可以调用const成员函数,但const对象不能调用非const成员函数
最后还有一个关键字,简单介绍一下:mutable
用来修饰数据成员,意为在const对象中,该关键字修饰的数据成员依然可以被更改
#include <iostream>
using namespace std ;
class Example{
int x_ ;
mutable int y_ ;
public :
Example(int x , int y) : x_(x) , y_(y) {}
void Add() {
x_ ++ ; //error
y_ ++ ; //safe because mutable
}
void Display() {
cout << x_ << y_ << endl ;
}
};
int main(void) {
const Example b(3,4) ;
b.Add() ;
b.Display() ;
return 0 ;
}