一、const变量
定义一个变量为const,也就是常量,说明这个这个量是不可修改的,既然不可修改,就说明在定义的时候就要初始化。初始化可以用常量初始化,也可以用普通的变量初始化。例如:
const int val = 10;
int tmp = 20;
const int val2 = tmp;
cosnt变量的存储以及作用域,先看一段代码:
#include <iostream>
using namespace std;
int n = 0;
const int totalVal = 10;
void func()
{
int tmp;
const int count = 10;
cout<<"count = "<<count<<" and address = "<<&count<<" and tmp address = "<<&tmp<<endl;
}
int main()
{
const int kk = 20;
int m = 2;
cout<<"Address of n = "<<&n<<endl;
cout<<"Address of totalval = "<<&totalVal<<endl;
cout<<"Address of kk = "<<&kk<<endl;
func();
func();
cout<<"Address of m = "<<&m<<endl;
return 0;
}
运行结果:
Address of n = 0x443010
Address of totalval = 0x44007c
Address of kk = 0x22ff74
count = 10 and address = 0x22ff40 and tmp address = 0x22ff44
count = 10 and address = 0x22ff40 and tmp address = 0x22ff44
Address of m = 0x22ff70
可以看到,n、totalVal在一个存储区域(全局变量区),而func中的count和tmp以及main()中的m、kk在一个区域(栈区),我们暂时可以推测,const变量的存储和访问特点与普通的变量没有太大区别。
修改一下代码实验一下:
#include <iostream>
using namespace std;
int n = 0;
const int totalVal = 10;
const int * func()
{
int tmp;
const int count = 10;
cout<<"count = "<<count<<" and address = "<<&count<<" and tmp address = "<<&tmp<<endl;
const int *p = &count;
return p;
}
void f()
{
int tmp = 0;
cout<<"Address of tmp = "<<&tmp<<endl;
}
int main()
{
const int kk = 20;
int m = 2;
cout<<"Address of n = "<<&n<<endl;
cout<<"Address of totalval = "<<&totalVal<<endl;
cout<<"Address of kk = "<<&kk<<endl;
cout<<"Address of m = "<<&m<<endl;
const int *p = func();
cout<<"p1 = "<<p<<" value = "<<*p<<endl;
f();
f();
cout<<"p2 = "<<p<<" value = "<<*p<<endl;
return 0;
}
结果是:
Address of n = 0x443010
Address of totalval = 0x4400a4
Address of kk = 0x22ff74
Address of m = 0x22ff70
count = 10 and address = 0x22ff40 and tmp address = 0x22ff44
p1 = 0x22ff40 value = 2088763392
Address of tmp = 0x22ff44
Address of tmp = 0x22ff44
p2 = 0x22ff40 value = 2088763392
可以看出,当函数执行完了以后,原来func中存放count的空间被释放了。确实是存放在栈区。因此,const变量的存储和作用域与普通变量相同。但是有一点特别,普通变量声明为全局变量的时,外部文件可以直接通过extern调用。而const全局变量默认为文件的局部变量,也是就只有文件内部可访问,外部无法访问,如果要外部文件可访问,需要在定义const全局变量时候使用extern关键字。
例如:
在file1.cpp中如下定义和初始化
extern const int val = 10;
在file2.cpp中才能调用:
extern const int val;
2.const引用
指向const对象的引用。
const int val = 10;
const int &reVal = val;
const对象不能使用非const引用指向。
例如:int &reVal2 = val; 就是错误的
const引用可以指向非const对象。
使用const引用的作用在于,可以通过引用读取对象,但是不能通过引用修改对象。但对象本身可能可以通过其他方式修改,见代码:
#include <iostream>
using namespace std;
int main()
{
const int conVal = 10;
int tmpVal = 5;
const int &conRe = conVal;
const int &tmpRe = tmpVal;
cout<<conVal<<" "<<conRe<<endl;
cout<<tmpVal<<" "<<tmpRe<<endl;
tmpVal = 20;
cout<<tmpVal<<" "<<tmpRe<<endl;
system("pause");
return 0;
}
结果:
10 10
5 5
20 20
可以看出,tmpVal可以由一个const引用tmpRe指向,但其值仍然是可以修改的。但是,如果在程序中使用tmpRe修改,就会报错,例如:tmpRe = 20;编译错误。
const引用有什么用处呢?我们都知道引用的作用就是不需要复制,特别是在函数传参的时候,可以提高效率,所以我们往往会使用类似void func(int &re)这样的函数原型,但是这里有个问题,如果我们想让re只读呢?那就要用高const引用了。定义这样的函数原型void func(cosnt int &re),这样,即免去了函数调用的传值消耗,也防止了参数本身被修改,又由于const引用可以指向非cosnt对象,因此,这样的原型对于非const的实参也是适用的。
3.const指针
const指针分为:指向const对象的指针,指向对象的const指针;指向const对象的const指针。下面看一段代码来了解这三种用法:
#include <iostream>
using namespace std;
int main()
{
const int conVal = 10;
const int conVal2 = 100;
int tmpVal = 20;
int tmpVal2 = 200;
const int *p1 = &conVal; //指向const对象的指针,指向了一个const对象
// int *const p2 = &conVal; error,指向普通对象的const指针,指向了一个cosnt对象
const int *p3 = &tmpVal; //指向cosnt对象的指针,指向了一个普通对象
int *const p4 = &tmpVal; //指向普通对象的cosnt指针,指向了一个普通对象
const int *const p5 = &conVal; //指向const对象的const指针,指向了一个const对象
const int *const p6 = &tmpVal; //指向const对象的const指针,指向了一个普通对象
cout<<*p1<<endl;
// *p1 = 1; error,不能通过p1修改指向对象的值
p1 = &conVal2; //更改指针指向对象
cout<<*p1<<endl; //现在输出的是conVal2的值
cout<<*p3<<endl;
// *p3 = 3; error,不能通过p3修改对象的值
cout<<*p4<<endl;
*p4 = 4; //通过指针p4修改conVal的值
cout<<*p4<<endl;
// p4 = &conVal; error,不能修改p4的指向
cout<<*p5<<endl;
// *p5 = 5; error,不能修改指向对象的值
// p5 = &conVal2; error,不能更改指针指向
cout<<*p6<<endl;
// *p6 = 6; rror,不能修改指向对象的值
// p6 = &tmpVal2; error,不能更改指针指向
system("pause");
return 0;
}
通过程序看:
(1)指向const对象的指针
这里的p1和p3都是这种指针,可以看到,指向cosnt对象的指针即可以指向const对象,也可以指向非const对象,这点与cosnt引用相同,不同的值,指向const对象的指针,可以更改其指向,这里p1就从原来指向conVal改成了指向conVal2,但是,不能通过p1和p3来修改所指向对象的内容。原因:指向const对象的指针,因为指向的是const对象,因此,不能修改对象内容,而const只限定了指向的对象为const,并没有限定指针本身为const,因此,指针本身可以更改自己的指向。指向const对象的指针还有一种声明格式:int const *p = conVal;这个和p1以及p3的声明是等价的。
(2)指向普通对象的const指针
这里的p2和p4都是这样的指针,可以看出,由于指针本身是指向非const对象的,因此,可以通过指针来修改指向对象的值,下面的p4就修改了tmpVal的值。如果用这种指针去指向了一个const对象,编译器将报错,因为cosnt对象不能修改,而用这种指针指向后便可以修改,这样将产生严重的安全问题,p2说明了这种用法是不可行的。由于是一个const指针,因此限定了指针本身是一个常量,因此就当指针指向一个对象之后,将不能再改变其指向,它将一直指向该对象。因此,指向普通对象的const指针必须在声明时初始化其指向。
(3)指向const对象的constt指针
包括了上面提到的两种限制,限制最严格。