const修饰变量
还是先从C谈起吧,在学习C语言的时候就知道const修饰的是常量,其值是不可改变的,真的是这样吗?
[cpp] view plaincopyprint?
int main()
{
const int a = 1;
int *b = (int *)&a;
*b = 2;
printf("a = %d\n",a);
return 0;
}
结果是2,但是a = 2则会报错,表明在C中const修饰的只是只读变量,本质上是可以被修改的!那么在C++里面呢?稍微修改一下上面的代码。
[cpp] view plaincopyprint?
int main()
{
const int a = 1;
int *b = (int *)&a;
*b = 2;
printf("a = %d\n",a);
printf("*b = %d\n",*b);
printf("&a = %0x\n",&a);
printf("&a = %0x",b);
return 0;
}
咋一看,很奇怪的结果啊b与&a的值明明是一样的,怎么对其取值却不同呢?其实在C++中const才成为了真正的常量,对const修饰的变量不会为其分配内存空间,每次取用其时都会用符号表里面的值直接替换,但是为了兼容c语言在程序中有&a时还是会为其分配内存空间(当const常量为全局变量并需要在其他文件中引用或对其使用&取地址时会为其分配内存空间)。
由上可见在C++中const修饰的变量类似于宏定义,区别是:
const常量是由编译器处理的,提供类型检查和作用域检查;
宏定义由预处理器处理,单纯的文本替换
另外默认情况下C中的const是外联的,C++中时内联的,因此在C++中若要使用其他文件中定义的const常量则必须使用extern关键字。
该节的最后再介绍一下const退化。
[cpp] view plaincopyprint?
int main()
{
int temp = 1;
const int b = temp;
volatile const int c = temp;
int *a1 = (int *)&b;
int *a2 = (int *)&c;
*a1 = 2;
*a2 = 3;
cout<<b<<endl;
cout<<c<<endl;
cin.get();
return 0;
}
从输出结果来看这里的const常量b,和c貌似和前面提到的C中的const常量一样,退化为了只读变量,这里解释一下,使用volaitle之后,该变量使用时每次都从内存中取值,故其退化为只读变量,对于变量b由于在编译期间不能直接确定其值故也退化为了只读变量!
const修饰指针
这里没什么好说的,左数右指,const在*左边,a指向的内容不可修改,右边指针本身不可被修改。
[cpp] view plaincopyprint?
const int *a; <span style="white-space:pre"> </span>
int const *a; //*a不可改变
int *const a; //a不可变,需在定义的时候初始化
const int *const a; //a及*a均不可变,需在定义的时候初始化
const引用
这里只是简单介绍一下,下面会有更详细的说明
[cpp] view plaincopyprint?
int main()
{
const int &a = 1; //这里会为1分配内存空间,&指向这片内存空间
int *b = (int *)&a; //
*b = 2;
cout<<a<<endl;
return 0;
}
先不管引用在编译器内部的实现(int &p ==const int *p)我们在使用的时候把他当成别名就可以了。有了上面的解释相信最后打印出2的结果也就不难理解了。
下面继续,看一下两段小程序
[cpp] view plaincopyprint?
const int a = 1;
int &b = a;
int a = 1;
const int &b = a;
编译后发现第一段是错误的,这个是很好理解的,非const的引用怎么能用const变量初始化呢,那么下面的呢?
[cpp] view plaincopyprint?
float a = 1.0;
int &b = a;
结果是编译不通过,但是如果改为const int &b = a;编译就可以通过了,比较奇怪?考虑其在编译器中的实现,以上代码翻译为
[cpp] view plaincopyprint?
float a = 1.0;
int temp = a;
int &b = temp;
那么对b进行修改原意是修改a,但是从上面的翻译中修改b只会对temp临时变量的值进行修改,而不会对a产生任何作用,因此C++要求此种情况下引用类型必须为const,这样就不会产生模糊不清的状况了。
函数中的const
[cpp] view plaincopyprint?
void function(const int Var); //传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)
void function(const char* Var); //参数指针所指内容为常量不可变
void function(char* const Var); //参数指针本身为常量不可变(也无意义, 因为char* Var也是形参)
[cpp] view plaincopyprint?
const int fun1() //无意义,参数返回本身就是赋值。
const int * fun2() //即指针内容不可变。
int* const fun3() //指针不可变,无意义
类中的const
[cpp] view plaincopyprint?
void function(const Type &Var);
const修饰引用时就变得很有必要了一是表明其在函数内为常量不可变,二也是比较常见的,我们都知道C++中的拷贝构造函数及赋值函数都是用了const引用,例如下例(Test表示类)
[cpp] view plaincopyprint?
const Test a;
Test b;
a = b;
如果在赋值函数中没有const,上述的编译还能通过吗?(不能,原因const引用中已经介绍过)
终于到最后了,类中的const变量
在类中,有定义
[cpp] view plaincopyprint?
const int val;
那么该val怎么初始化呢?直接初始化时不行了,C++不允许这样做。C++中有初始化链表,可以在调用构造函数时对其进行初始化,初始化链表的调用顺序与在链表的顺序无关,只与起申明顺序有关,另其调用在构造函数前。
用法
构造函数后
[cpp] view plaincopyprint?
Test():val(1)
{
...
}
当然按前面讲的,这里的const常量,已经退化为const只读变量了。
类中的const函数
[cpp] view plaincopyprint?
class Test
{
private:
int val;
public :
void setValue()const
{
val = 1;
}
};
const在函数末尾表示该函数内部不能对类中变量进行修改,因此上述代码是不可能编译通过的,在C++中变量定义前对需要修改的变量前面加上mutable即可修改!
|