被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.这是我们最常看到的一句话。但是我们常常将const和其他技术相结合使得程序更加优秀。
1.修饰变量
1.1 其中修饰变量也有很多种情况,我们先来分析内存的不同情况
代码:
#include
using namespace std;
int i=0;
static int j = 0;
int k;
const int a=1;
const static int b = 2;
int main()
{
const int c=1;
const static int d=2;
static const int e=3;
printf("%p\n",&i);
printf("%p\n",&j);
printf("%p\n",&k);
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
printf("%p\n",&d);
printf("%p\n",&e);
}
运行结果:
分析:
我们根据内存模型详解https://editor.csdn.net/md/?articleId=121130997分析i,j,k都是分配在全局静态区。我们观察到a,b的地址与i,j,k
有很大的不同。a,b应该在内存的常量区。这就说明在函数外定义变量只要加上const除了可以让变量变得不可修改还可以改变变量内存分配的实际位置。我们观察c,d,e,我们发现从d,e与a,b的地址相近,所有d,e是在常量区,那么c就只可能在栈区了。所有在函数内给变量const可以让变量变得不可修改,如果要改变变量分配区域需要加上static(const static顺序无关)
小结:
const基础作用,防止修改值(可以取代宏定义,变得类型安全)
在函数外加上const 内存分配区从全局区变成常量区
在函数内加上const 内存依然在栈区,除非同时加上static(顺序无关)
我们使用const_cast去掉const,继续测试
#include
using namespace std;
int main()
{
const static int d=2;
const int i = const_cast(d);
int j = const_cast(d);
static int k = const_cast(d);
printf("%p\n",&d);
printf("%p\n",&i);
printf("%p\n",&j);
printf("%p\n",&k);
}
运行结果:
我们知道:
d分配在常量区,
我们用const_cast去掉static后内存分配在栈区,
用 const_cast去掉const和static也分配在栈区
我们用const_cast只去掉const内存分配在静态区
综合上述测试结果,我们得出结论:
函数外:
没有const,没有 static,分配在全局区;
没有const,有 static,分配在全局区;
有const,没有 static,分配在常量区;
有const,有 static,分配在常量区;
函数内:
没有const,没有 static,分配在栈区;
没有const,有 static,分配在全局区;
有const,没有 static,分配在栈区;
有const,有 static,分配在常量区;
1.2 const修饰一般参数作为形参
代码:
#include
using namespace std;
void func(int a,int b,const int c)
{
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
}
const int a=1;
int main()
{
const int b=1;
printf("%p\n",&a);
func(a,a,a);
printf("\n");
printf("%p\n",&b);
func(b,b,b);
}
运行结果:
分析:根据上文,a,b分配在常量区和栈区.我们调用函数,很明显,形参全是分配在栈上。
2.const修饰引用
测试代码:
#include
using namespace std;
int i=1;
const static int & j = i;
const int & k = i;
int main()
{
int a=1;
const static int & b = a;
const int & c = a;
printf("%p\n",&a);
printf("%p\n",&b);
printf("%p\n",&c);
printf("%p\n",&i);
printf("%p\n",&j);
printf("%p\n",&k);
}
运行结果:
分析 我们发现const 修饰引用的时候,在函数内外都不能改变实际分配的地址,只能保护该地址的值不能修改。这样就有一个好处,我们可以把函数形参改成常引用的形式,这样我们既有引用的效率优势(别名)又有const的安全性(不可修改)
3.const修饰指针
const 修饰指针分为四种情况(实际上就是三种):
const 变量类型 * 变量名;
变量类型 const * 变量名;
变量类型 * const 变量名;
const 变量类型 * const 变量名;
#include
using namespace std;
int main()
{
int a=1;
const int * p1 = &a;
int const * p2 = &a;
int * const p3 = &a;
const int * const p4 = &a;
//*p1=1;,错误,不能修改指针指向的值
p1++;
//*p2=1;错误,不能修改指针指向的值
p2++;
*p3=1;
//p3++;
//*p4=1;错误,不能修改指针指向的值
//p4++;错误,不能修改指针指向的地址
}
分析;
当我们将const 放在星号之前,我们不能修改指针指向的值
当我们将const 放在星号之后,我们不能修改指针指向的地址
当我们将const 放在星号之前和之后,我们不能修改指针指向的值和地址
方便记忆:
有两种大致情况
const 星号 变量名 = const (星号 变量名) = const (值) = 不能修改指针指向的值
星号 const 变量名 = 星号 const ( 变量名) = 星号 const (地址)= 不能修改指针指向的地址
4.修饰函数返回值
这个没什么好说的,返回值是不能修改的,如果返回的是常量指针对应上面第三种情况
5.修饰成员函数
被const修饰的成员函数叫做常成员函数;
先说一下类中哪些函数不能被const修饰吧:
构造函数不能
析构函数不能
拷贝构造函数不能
可以设置为const 的函数:
普通成员函数
虚函数
纯虚函数
重载操作符函数
来说一下const修饰函数的作用吧
防止成员变量被修改,,原理是将this指针改成const,表明在该成员函数中不能对类的任何成员进行修改。
使用方法:
class A
{
int a=1;
public:
void foo(int& b) const
{
b=1;
}
};
class B:A
{
int a=1;
void func()
{
foo(a);//可以指向
}
};
分析:我们发现const,只能包含本类的成员不被修改,不能保证子类不修改子类成员(const保护的是本类的this指针!)