C++中const的详解

  • const的含义
    使用const用来表示程序中不可修改的常量或对象的值。

  • const作用
    (1)const可以定义常量。例如,const int a = 100;其中定义了a为常量,不可以修改。
    (2)类型检查。const常量具有类型,编译器可以进行安全检查,#define也存在类型,不过与其表达量有关(整数,浮点数,用户定义以及运算符的结果类型)。const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。但是如果用const表达常量时必须用常量表达式赋值,使用它时不需要依赖它为变量的身份,一定场合下不需要定义,比如类中的static成员对象。编译器在作为常量处理它时,不会依赖“一份定义”,而是像是立即数一样使用它,它本身可能在机器码中被“拷贝”到多个地方,和 #define 定义的宏常量的结果相同。另一方面, const 定义的常量由于是整数或枚举,所以直接变成机器码上的立即数往往性能更好。最后,const 定义的变量只有类型为整数或枚举,且以常量表达式初始化时才能作为常量表达式。其他情况下它只是一个 const 限定的变量,不要将与常量混淆。

    综上,const在程序周期中是被程序以一段内存地址来存储的,使用时调用其内存地址,但是#define使用的是它的拷贝,使用一次拷贝一次,这样const对内存的使用很灵活。

  • 防止修改,保证程序的健壮性。
    比如下面的代码:

void Demo(const int num)
{
	num++;
}

这样编译器就会报错,表达式的值必须为可修改的左值。这样可以保证num的值不被恶意修改。

  • 可以节省内存
    因为const给出的是常量的内存地址,使用时直接调用内存地址,直接使用,不用进行拷贝。

  • const对象默认为文件局部变量
    注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。
    未被const修饰的变量不需要extern显式声明!而const常量需要显式声明extern,并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。

  • 指针与const
    与const有关的指针有以下四种,

    const int *ptr;	//指向常量的指针
    int const *ptr;	//同上
    int * const ptr;	//内存地址为常量的指针,又叫常指针,const指针
    const int * const ptr;	//指向const对象的const指针
    

    这几种比较相似,但是区别也很大。最明显的区别就是*的位置。前两种表示修饰的是指针所指向的值,第三种修饰的是地址,最后一种修饰的是地址和指向的对象都是不可变的。const表示修饰的是常量,对于指针的理解我在前面文章中有过介绍。而且后两种在编译时会报错,具体原因后文会进行介绍。接下来我就这几种情况进行介绍。
    (1)指向常量的指针。

const int *ptr;
int const *ptr;

这两种情况表示指向常量的指针,表示它指向的对象不可变。const修饰的是int,也就是ptr指向的类型,而不是ptr本身,所以ptr不必赋初始值。但是不能通过修改ptr来改变对象的值。因为其对象是常量,不可变。

const int *ptr;
*ptr = 10//error

除此之外,也不能用void*来储存const对象的地址,必须使用const void *类型的指针来储存对象的地址。

const int *p = 10void *q = &p; //error
const void *q = &p; 

另外还要一个需要注意的地方,即允许把非const对象的地址赋给指向const对象的指针。例如

const int *p;
int q = 10;
p = &q;	//ok

但是不能通过修改p的值来修改q的值,即使它指向的是非const对象,因为p指向的对象为常量。
但是修改指针可以使用另一种方式。例如:

int *p = 10*p = 4;
cout<<*p<<endl;		//the output is 4

(2)常指针
const指针必须初始化,且const定义的内存不可修改。而且其内存必须初始化,例如下面的例子:

int t = 0int n = 100int* const ptr = &t;
* ptr = &n;

这样是可以的,即可以修改它指向的对象的值。
最后,当把一个const常量的地址赋值给一个用const修饰的地址时,需要把指向的对象也设置为const。例如下面的例子:

const int num = 10;
	int* const p = &num;

这样就会报错,“const int *” 类型的值不能用于初始化 “int *const” 类型的实体,原因是p指向的对象为一个const常量,但是int前没有加const,所以不可以通过编译。只要这样修改:

const int* const p = &num;

这样它指向的就是一个常量,就可以通过编译。
(3)
指向常量的常量指针地址
有了前面的介绍,就可以理解什么是指向常量的常量指针地址。

  • 函数中的const

const可以修饰返回值
(1)const int fun1();
但是这样做明显没有意义,因为函数的返回值本来就是赋值给变量的。
(2)const int * fun1()
这样表示指针指向的内容不变
(3)int * const fun1()
这样表示指针本身不可变。

const可以在参数传递中发挥作用。比如修饰参数,使其在函数体中不可变,但是其意义不是很大,一个函数基本上就是参数进行修改操作的。例如下面的几个函数:

void fun1(onst int num);
void fun2(int *const num);

这两个函数中不可以修改num的值或者指针。
const还可以修饰,使其指向的对象不可变。例如:

void fun3(int const *num);

这样表示num指向的对象在函数中不可以改变。
参数为引用,防止其在函数中被错误或者恶意修改,这样明显增加了程序的健壮性。
假如函数为:void fun3(const A &a);这样在函数内部就保证了a的不变。
为什么要这样做呢?我们从以下角度来考虑,假如函数为void fun3(A a);这样在函数使用时必定会产生一个拷贝,使用时复制一个对象,而且在析构时也会再次析构,这无疑增加了程序的复杂性,因此使用引用可以节省开销,但是我们如果要保证它在函数中不可以被改变的时候,就需要增加const来保证它是不可以改变的。
但是以此类推,如果是void fun1(int num);也需要变为void fun1(const int &num);吗?答案是否定的。因为对于内置数据类型,函数在拷贝,析构等操作时,即使会增加开销,但是也很快,这个速度与引用的速度相当,所以只能说没必要,传入参数就可以。

  • 类中使用const
    这是最后一个const的使用方式了,在类中使用const常量,则其不可改变,而且不可以被普通函数调用,只有常成员函数可以进行调用,这无疑增加了程序的健壮性。意思是const定义的函数才可以调用const定义的常量。还有常成员对象只可以访问常成员函数,但是普通对象却可以调用类中所以函数,常成员函数也可以调用。

综上所述,const其实就是修饰了一个常量,使其不可以改变,而且有一定的就近原则(具体还要在实际环境中考虑),它在程序的安全性中确实有很重要的作用,所以我认为应该大胆的使用const进行编程。以上便是我对const的一些个人的认识,希望对大家有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值