1.const位置
//等价,即v的值初始化之后就不能改变
const int v;
int const v;
//前者const修饰int*,表示指针P本身的值是不能改变的(指针常量)
//后者const修饰int,表示指针指向一个整形常量,P可以修改所指向的对象
int *const p;
int const *p;
//P1不是指针常量,指向整形常量
//P2不是指针常量,指向指针常量
int const **p1;
int* const *p2;
int const val = 1;
int & const r = val; //错误,此时const修饰的是int&,即引用本身,而引用本身相当于是一个指针常量,初始化和一个对象绑定之后便不能改变所绑定的对象
const int& r = val;//正确,const不可少,用来修饰r所绑定的对象是一个整形常量
2. const对象和对象的const成员
用const来定义一个基本类型的变量,是指不允许显示修改变量的值。如果用const来定义某个类的对象,则情况还要复杂一些,因为对象除了有成员数据之外,还有成员函数。用const修饰的对象称为常对象,用const修饰的成员函数称为常函数,在常函数中不允许对任何成员变量进行修改。通过常对象,也只能调用该对象的常函数。
1)如果常函数的声明和定义分开进行,那么在两边都要使用const关键字,否则发生编译错误。
2)只有类的非静态成员函数才可以被声明为常函数。不是类的成员函数,比如外部函数等,是不能被声明为常函数的。
3)一个类的两个成员,如果函数的返回值类型、函数名、函数的参数列表完全相同,一个是常函数,一个是普通函数,那么它们之间构成重载关系。非const对象调用某个函数时,先寻找它的非const函数版本,如果没有找到,在调用它的const函数版本。常对象,只能调用类中定义的常函数,否则编译器会给出错误信息。
//构成重载关系
void disp(); //void disp(this)
void disp() const; //void disp(const this)
4)如果一个非const对象调用某个函数时,这个函数同时存在const和非const,我们希望调用的是const该怎么办呢?我们必须通过建立该对象的常引用或指向该对象的常指针来达到目的。
A a;
((const A&)a).disp();
((const A*)&a)->diap();
5)常对象在创建之后,其数据成员的值便不允许再修改。所以常对象的数据成员初始化工作很重要,必须定义合适的构造函数使对象的数据成员拥有有意义的初始值。如果一个类没有显示定义默认构造函数,在程序中调用默认构造函数生成该类的常对象,这种做法会遭到很多编译器的拒绝。因为编译器提供的默认构造函数并没有给类对象的数据成员提供任何有意义的初试值。定义一个常对象,是将该对象的全体数据成员当作常量看待。而一个非只读对象中,也可以将部分数据成员定义为常量,这就是类对象的常量成员。类对象的非静态常量成员必须在构造函数中初始化,而且只能借助初始化列表进行。
6)修饰函数参数和返回值:const修饰参数时,主要将被引用对象或指向的对象声明为常量,如果将传值调用声明为常量,则没有多大使用价值。不是说不行,只是值传递本身不会影响原来的变量值,所以这么做没有意义。
3. 常引用或常指针
只是说明不能通过该引用去修改被引用对象的值,至于被引用对象原来是什么性质,是无法由常引用常指针来决定的。即,常引用可以绑定非const对象,const对象必须使用常引用绑定。
4. const_cast
c++运算符,作用是去除复合类型中的const或volatile属性。但它没有改变只读变量本身的只读属性。const_cast取消的是对间接引用是的改写限制,而不能改变变量本身的const属性。
const int a = 5;
int* p;
p = const_cast<int*>(&a);//如果直接写成 p = &a; 则会发生变异错误
5. mutable
c++关键字。
作用:能够在保持常量对象中大部分数据成员仍然是“只读”情况下,实现对个别成员数据的修改。
1)mutable只能作用于类的非静态和非常量数据成员。
2)提示编译器该变量可以被类的const函数修改。