const用法
const是c++的一个关键字,C语言也采用了const关键字,但是完全没有发挥出const的作用,const主要是在c++中被广泛采用。
-
const的作用:
- const的最开始的被主要是被用来代替define,因为define有很多弊端,例如只能在预处理阶段进行代替,代替的数据并没有类型信息,对代码维护很不友好,而且没有类型检查功能。而const则解决了这些功能,当然只是在c++里,在C语言里,const与c++有很大的差别。
#define N 10
与const int N = 10;
实现的功能基本相同,只不过define在预处理阶段完成,const在编译阶段由c++编译器完成。 - const既然是常量,显然可以用于保证代码的安全性,在明知道在变量生命周期内是不变的时候可以加上const设为常量,避免外界代码改变变量的值。
- const的最开始的被主要是被用来代替define,因为define有很多弊端,例如只能在预处理阶段进行代替,代替的数据并没有类型信息,对代码维护很不友好,而且没有类型检查功能。而const则解决了这些功能,当然只是在c++里,在C语言里,const与c++有很大的差别。
-
const的用法:
调用const的最简单语法就是const int a = 0;
在c++中const默认是内部连接 ,即只能在当前文件内使用a这个常量。
关键点:- 在上面这个语法中的常量必须要初始化。
- 此语法在编译器中并不为他分配存储空间,而是把这个定义存储在符号表里。
- 在使用时会进行常量折叠。类似宏替换。
较为明显的特征就是char c[a];
是符合语法的。如果是a是变量的话,是不能用来声明数组的。
当然并不是只要是const修饰的变量就不分配内存,当需要用到这个变量的地址时例如
int address = & a;
int & b = a;
或者是加上extern修饰时
extern int a = 0;
都会强制编译器为a分配内存。
分配内存会影响常量折叠,但不是绝对的。
比如
const int a = 100;
int address = &a;
int b[a];
这仍然是合理的,因为虽然取地址会强制为a分配内存,但是在之前编译器已经认识i是一个常量且值为一百,已经将他加入符号表中。所以仍然可以。
如果是
const int a[] = {1,2,3};
int b[a[1]];
就不合理,因为此时编译器并不会将一个数组作为一个常量并加入如符号表中。所以编译器只会为a分配不可改变的存储空间。所以a[i]并不会进行常量折叠。
加上extern后还有一些其他的额外用法
首先extern修饰后在文件外也可以用这个常量。
分为初始化和不初始化两种:
初始化
extern int a = 0;
初始化的情况下在本文件中依然可以进行常数折叠。
不初始化
extern const int a;
不初始化的情况下本文件中不能进行常熟折叠。虽然是const但是仍然可以,这里看成是声明,而且需要在外部实现定义。
除了基本的int 、float、char、double类型const还可以和指针、函数、类结合在一起用,非常容易混淆。
- 与指针一起用:
可以组成指向常量的指针和常量指针- 指向常量的指针:
const int *a;
int const *a;
- 常量指针:
int a = 1;
int * const address = &a;//首先常量指针必须赋初值,这与前面是一样的。
可以总结为const总是优先修饰前面的变量类型,如果前面没有才修饰后边的。
结合起来有
const int a; //这里即使是非const的变量也可以进行下面的赋值。
int const * const address1 = &a;
const int * const address2 = &a;
- 与函数一起用
- 作为参数
值传递的话其实并没有太大的意义,只是防止函数对副本的改变,倒不如在函数内部约定。
指针地址传递的话可以防止对原变量的修改。 - 作为返回值
对于内建变量(int 、float、char、double)无关紧要,因为返回时也是返回的副本,但是const只要求原变量不变。但是对于自建类就有区别,返回的const值必须不能复制、不能修改。还需要注意的重点是临时量。
- 作为参数
class A
{
public:
int a;
X(int a1 = 0){a = a1};
};
A f1()
{
return A();}
A f2(A &x)//这里只能是接受const的引用,指针不行,因为指针必须明确接受地址,而下面调用明显是直接传给他临时对象。
{
return x;
}
f2(f1());
在f2(f1());中f1()的返回值虽然不是const但是为了将返回值传递给f2();会创建临时量,而这个临时量由于只有系统管理人无法获得或改变,所以默认const,传给f2()是付给了一个非const,所以就会报错。
- 与类一起用
- 类里的常量
类里面的常量并不能进行常量折叠,而是像C语言中const的用法一样就是不能被更改的存储空间。每一个类对象都有这样一个常量,但是常量的值可以不同,所以在声明const成员是不能初始化。类里const成员的初始化要在构造函数的初始化列表里初始化,因为初始化列表是在构造函数体执行前完成的。
如果想要实现常量折叠,需要在加上static静态关键字,static意味着这个常量并不为某个对象所独有,所有该类的对象共同拥有这个常量,而且值相等。此时需要在类的定义的时候就初始化,而不是创建对象时才初始化。
- 类里的常量
- const类和const成员函数
由于const成员函数的出现于const类有很密切的联系,所以放在一起说。
const函数的作用要从const类说起,const类要求所以数据成员在对象的存在周期内不得改变,所以这个对象只被允许调用那些不改变对象内数据成员的成员函数,为了编译器识别出这些函数,我们必须用const标记成员函数。语法为:
class A{
public:
int a;
A(int a1 = 0):a(a1); //构造函数和析构函数不是const类型的,因为这两个函数都是默认会改变类的数据成员的。
int geta() const;
};
int geta() const
{return a;}
为区分返回为const,const需放在函数后面。而且定义是也必须加上const否则视为不同的函数。
注意:const成员函数不能以任何方式改变数据成员,也不可调用非const成员函数。
为了增强代码的灵活性,有时const对象也不一定是都不变的,也可能是大部分不变但是一少部分需要改变。所以有mutable关键字。加上mutable关键字的数据成员可以在const对象的const成员函数中改变。
附:
volatile与const的语法一样,意思是在编译器认识的范围外,这个数据是可以改变的。
const在c++与在c中的差别:
在c中const只是一个不能改变的存储空间。
在c++中有了更多的可能,也可以和一样,也可以分配了空间但是仍然可以常量折叠,也可以不分配空间进行常量折叠。除此之外还有与类的组合应用。