C/C++中const关键字

const经常被用到,所以今天总结一下const关键字的用法,常用的场景为修饰变量(C/C++不同),修饰指针和引用,修饰函数参数,修饰函数返回值,修饰函数定义体(类 成员函数)

(1)修饰变量

cosnt在修饰变量时,在C和C++中是不同的。

const int a = 5;
int array[a];//在C语言中是错误的,因为在C语言中是定义了一个只读变量
int array[a];//在c++中是正确的,因为在C++中定义了一个常量
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

C中,const是一个不能被改变的普通变量,既然是变量,就要占用存储空间,所以编译器不知道编译时的值.而且,数组定义时的下标必须为常量.

而C++中的const正常情况下是看成编译期的常量,编译器并不为const分配空间,只是在编译的时候将期值保存在名字表中,并在适当的时候折合在代码中. 所以在C++中const修饰的量可以用在数组的定义中。

在C语言中: const int size; 这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间. 
但在C++中这样写是不正确的,会报错 
这里写图片描述 
C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字. C++中,const默认使用内部连接.而C中使用外部连接.

内连接:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定.

外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern关键字声明,可以从其他文件访问相应的变量和函数. C++中,是否为const分配空间要看具体情况. 如果加上关键字extern或者取const变量地址,则编译器就要为const分配存储空间.

C++中定义常量的时候不再采用define,因为define只做简单的宏替换,并不提供类型检查.

(2)修饰指针和引用

2.1修饰指针

使用const修饰指针时,由于const的位置不同,而含意不同。如果const位于的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于的右侧,const就是修饰指针本身,即指针本身是常量。 
可能有点绕,举几个例子来说明一下。

int a = 10;
    int b = 20;
    //const在*左边,常量指针
    const int * p1 = &a;
    int const *p2 = &a;
    *p1 = 30;//错误,因为const修饰的是p1指向的值,不能通过p1修改,但是可以通过a来修改。
    a = 20; //正确

    //const 在*右边,指针常量
    int * const p3 = &a; 
    p3 = &b; //错误,因为const修饰的是p3指针,所以p3的指向不能修改
    *p3 = 20; //正确
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.2修饰引用 
 使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下: 
  const <类型说明符> & <引用名> 
  例如: 
  const double & v; 
  在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。这个看下面的修饰函数参数时再详细讲解

(3)修饰函数参数

首先如果该参数用于输出,那么无论是采用指针传递还是引用传递,都不能加const修饰。所以const只能用于修饰输入参数。这里又分三种情况:输入参数采用值传递还是指针传递还是引用传递。

3.1采用值传递 
如果采用值传递,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不需要加const 修饰。 
例如,对于函数void Func1(int x),写成void Func1(const int x)一点意义也没有。同理,对于void Func2(A a)也不需要写成void Func2(const A a),其中A为用户自定义的对象类型。

3.2采用指针传递 
如果采用指针传递,那么加const可以防止函数体内部对该参数进行改变,起到保护作用。 
例如某个 StringCopy 函数: 
void StringCopy(char *strDestination, const char *strSource); 
其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图改动 strSource 的内容,编译器将指出错误。

3.3采用引用传递 
首先我们来说一下,为什么要引入引用传递这种方法。原因是:对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void Func(A& a)。这样一来,根据引用传递的定义,只是借用了参数的别名,不需要产生临时对象。 
但是,这样一来,当函数体中改变了参数a的值后,相应的传递的原始值也会相应改变。所以如果不希望改变原始参数,只需要在前面加上const修饰,这样一来,函数最终定义为void Func(const A& a)。同理,是否应将void Func(int x) 改写为void Func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。 
总结一下const作为函数输入参数的用法:

  1. 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。

  2. 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。

(4)修饰函数返回值

根据上面的思路,这里也分三种情况,即值传递、指针传递、引用传递。

  1. 如果函数返回值采用“值传递”方式,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。 
    例如,不要把函数int GetInt(void) 写成const int GetInt(void)。同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
  2. 如果函数返回值采用“指针传递”方式,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。 
    例如,定义函数为:const char *GetString(void),那么char *str = GetString()将会出现编译错误。应该写成const char *str = GetString()。

  3. 如果函数返回值是采用“引用传递”方式,它的意义在于能提高效率,而这种方式使用场合并不多。这个时候,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。

    例如,对于**类的重载赋值函数**A & operate = (const A &other),如果不加cons修饰,则定义A a, b, c;(a = b) = c,程序合法,但是如果加上const修饰,即const A & operate = (const A &other),则程序会报错。

(5)修饰成员函数

const 成员函数的声明看起来怪怪的:const 关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。 
但事实上,任何不会修改数据成员(即函数中的变量)的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

例如下面的类MyClass的成员函数getCount():

class MyClass {
    int member ;
    public:
    int getCount ( ) const;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里,在函数定义头后面加上的 const 表示这个函数是一个“只读函数”,函数不能改变类对象的状态,不能改变对象的成员变量的值。如在函数体中不能这么写:

classname :: getCount( )
{   member =4 ;
    return member;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

编译器将指出getCount 函数中的错误,因为试图修改数据成员member。同样,const成员函数也不能在函数中调用其他非const 的函数。

总结一下const成员函数:

1. const对象只能调用const成员函数 
2. 非const对象可以调用const和非const成员函数,不过优先调用非 const成员函数 
3. 对于加上mutable修饰符的数据成员,const成员函数可以修改

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值