C/C++中const关键字的用法及其与宏定义的比较

1.const关键字的性质

简单来说:const关键字修饰的变量具有常属性。 即它所修饰的变量不能被修改。

2.修饰局部变量

1 const int a = 10;
2 int const b = 20;

这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了,而且编译器也不允许不赋初值的写法:

在C++中不赋初值的表达一写出来,编译器即报错,且编译不通过。

在C中不赋初值的表达写出来时不报错,编译时只有警告,编译可以通过。而当你真正给它赋值时才会报错,那么没有初值也不能被赋值这样的变量有什么用哪?

1 const chsr* p = "qwerty"; //const用于修饰常量静态字符串

如果没有const的修饰,我们可能会在后面有意无意的写p[4]=’x’这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。(这个特性在C/C++中相同)

3.修饰指针

常量指针是指针所指向的内容是常量,不可被修改。

1 const int * n = &a;
2 int const * n = &a;

上面两种写法也是等价的,性质如下:

1)常量指针通过不能这个指针改变变量的值,但是可以通过其他的引用来改变变量的值的。

1 const int *n = &a;
2     *n = b;

上面的写法报错

1 int c = 3;
2 const int *n = &a;
3 a = 10;
4 a = c;

这样赋值是可以的。

2)常量指针指向的值不能改变,但是指针本身可以改变,即常量指针可以指向其他的地址。

1   int a = 1;
2    int b = 2;
3     const int *n = &a;
4     n = &b;

指针常量是指指针本身是个常量,不能在指向其他的地址,写法如下:

1     int a = 1;
2     int b = 2;
3     int * const n = &a;
4     *n = b;5      b = a;

而这么写是错误的

1 int a = 1;
2 int b = 2;
3 int c = 3;
4 int * const n = &a;
5 n = &b;

它们的区别在于const的位置,可以这样记忆:const在“*”前面时它修饰(*n),而*n是n所指向的变量,所以是常量指针,const在“*”后面时它修饰(n),使指针变为常量,所以是指针常量。

指向常量的常指针

1 const int * const p= &a;
2 int const * const p= &a;

指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过变量赋值,或其他的普通指针改变变量的值。

(这种用法在C和C++中是相同的。)

4.修饰引用

1 int a = 1;
2 int const &a = b;
3 const int &a = b;

两种定义形式在本质上是一样的

5.函数中使用const

(1)修饰函数参数

根据const修饰指针的特性,const修饰函数的参数也是分为三种情况

1 void StrCopy(char *strdes, const char *strsrc);//防止修改指针指向的内容

其中 strsrc是输入参数,strdes是输出参数。给 strsrc 加上 const 修饰后,如果函数体内的语句试图改动 sresrc 的内容,编译器将指出错误。

1 void swap ( int * const p1 , int * const p2 )  //防止修改指针指向的地址

指针p1和指针p2指向的地址都不能修改。

1 void test ( const int * const p1 , const int * const p2 )  //以上两种的结合

另外当参数为引用时

1 void function(const Class& Var); //引用参数在函数内不可以改变
2 void function(const TYPE& Var); //引用参数在函数内为常量不可变 

(这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙。)

(2)修饰函数返回值

如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。

1 const int * fun2()    //调用时 const int *pValue = fun2(); 
2                             //我们可以把fun2()看作成一个变量,即指针内容不可变。
3 c.int* const fun3()   //调用时 int * const pValue = fun2(); 
4                             //我们可以把fun2()看作成一个变量,即指针本身不可变。 

const int fun1()   //这个其实无意义,因为参数返回本身就是赋值。

6.修饰类相关

(1)用const修饰的类成员变量,只能在类的构造函数初始化列表中赋值,不能在类构造函数体内赋值。

 1 class A
 2 {
 3 public:
 4     A(int x) : a(x)  // 正确
 5     {
 6          //a = x;    // 错误
 7     }
 8 private:
 9     const int a;
10 };

(2)const修饰成员函数

用const修饰的类成员函数,在该函数体内不能改变该类对象的任何成员变量, 也不能调用类中任何非const成员函数。一般写在函数的最后来修饰。

 1 class A
 2 {
 3 public:
 4     int& getValue() const
 5     {
 6         // a = 10;    // 错误
 7         return a;
 8     }
 9 private:
10     int a;            // 非const成员变量
11 };

a. const成员函数不被允许修改它所在对象的任何一个数据成员。

b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

(3)const修饰类对象/对象指针/对象引用

用const修饰的类对象表示该对象为常量对象,该对象内的任何成员变量都不能被修改。对于对象指针和对象引用也是一样。
因此不能调用该对象的任何非const成员函数,因为对非const成员函数的调用会有修改成员变量的企图。

 1 class A
 2 {
 3  public:
 4     void funcA() {}
 5     void funcB() const {}
 6 };
 7 int main
 8 {
 9     const A a;
10     a.funcB();    // 正确
11     a.funcA();    // X
12
13     const A* b = new A();
14     b->funcB();    // 正确
15     b->funcA();    // X
16 }

(4)在类内重载成员函数

1 class A
2 {
3 public:
4     void func() {}
5     void func() const {}   // 重载
6 };

7.修饰全局变量

全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,以为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样方式不必要的以为修改,使用的方法与局部变量是相同的。

8.const与宏定义的区别

(1).便于进行类型检查

const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

1 //例子:
2 void f(const int i) { .........}   //对传入的参数进行类型检查,不匹配进行提示

(2)可以节省空间,避免不必要的内存分配

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

1 #define PI 3.14159         //常量宏
2 const doulbe  Pi=3.14159;  //此时并未将Pi放入ROM中
3               ......
4 double i=Pi;   //此时为Pi分配内存,以后不再分配!
5 double I=PI;  //编译期间进行宏替换,分配内存
6 double j=Pi;  //没有内存分配
7 double J=PI;  //再进行宏替换,又一次分配内存!

(3)提高了效率 

 宏定义是一个“编译时”概念,在预处理阶段展开,不能对宏定义进行调试,生命周期结束于编译时期。const常量是一个“运行时”概念,在程序运行时使用,类似于一个只读数据。

编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高

(4)可以保护被它修饰的东西

防止意外的修改,增强程序的健壮性。

1 void f(const int i) { i=10;//error! } //如果在函数体内修改了i,编译器就会报错
2      

(5)为函数重载提供了一个参考

1 class A
2 {
3            ......
4   void f(int i)       {......} //一个函数
5   void f(int i) const {......} //上一个函数的重载
6            ......
7 };

(6)定义域不同

 1 void f1 ()
 2 {
 3     #define N 12
 4     const int n 12;
 5 }
 6 void f2 ()
 7 {
 8     cout<<N <<endl; //正确,N已经定义过,不受定义域限制
 9     cout<<n <<endl; //错误,n定义域只在f1函数中。若想在f2中使用需定义为全局的
10 }

(7)做函数参数

  宏定义不能作为参数传递给函数;const常量可以在函数的参数列表中出现。

9.const_cast

const_cast运算符用来修改类型的const或volatile属性。
(1)常量指针被转化成非常量的指针,并且仍然指向原来的对象;
(2)常量引用被转换成非常量的引用,并且仍然指向原来的对象。

1 void func()
2 {
3     const int a = 10;
4     int* p = const_cast<int*> (&a);
5     *p = 20;
6     std::cout<<*p;    // 20
7     std::cout<<a;     // 10
8 }

注:C++中使用const 常量而不使用宏常量,即const 常量完全取代宏常量。

时间: 2024-08-05 03:05:35

C/C++中const关键字的用法及其与宏定义的比较的相关文章

C++中const 的各种用法

C++中const 关键字的用法 const修饰变量 const 主要用于把一个对象转换成一个常量,例如: const int size = 512; size = 0; // error: assignment of read-only variable 上面的例子中,定义size为常量并初始化为512,变量size仍是一个左值,但是现在这个左值是不可修改的,任何修改size的尝试都会导致编译错误. 因为常量在定以后就不能被修改,因此const对象定义时必须初始化,否则会引起编译错误,例如:

c++中const关键字全面总结

一.const作用 1.const定义常量 注意:const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用. (1)const修饰变量,以下两种定义形式在本质上是一样的.它的含义是:const修饰的类型为TYPE的变量value是不可变的. TYPE const ValueName = value; const TYPE ValueName = value; (2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初

c#多线程中Lock()关键字的用法小结

本篇文章主要是对c#多线程中Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助 本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待. 每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生.

mysql 去除重复 Select中DISTINCT关键字的用法

在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记录的所有值.其原因是 distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,我只有用二重循环查询来解决,而 这样对于一个数据量非常大的站来说,无疑是会直接影响到效率的.所以我花了很多时间来研究这个问题,网上也查不到解决方案,期间把容容拉来帮忙,

关于Java中final关键字的用法总结

用于数据 永不改变的编译时常量,必须是基本类型,static final常量定义时必须赋值 一个运行时被初始化却又不希望被改变的值 空白final,确保使用前必须被初始化,但有更大的灵活性 final参数,用于对象引用,对象不可改变,用于基本类型,值不可以改变 用于方法 防止方法的行为被改变,不可覆盖 private方法默认为final的 曾经使用final方法可以提高效率,现已不提倡 用于类 表示该类不可以被继承 final类方法默认指定为final的 关于Java中final关键字的用法总结

java中的关键字static(静态变量)和final定义常量

1 package point; 2 3 class Point { 4 5 int x = 0; 6 int y = 0; 7 static int z = 100; // 定义静态变量z,类变量 8 static final double PI = 3.1415926;// final定义常量,它的值在运行时不能被改变 9 10 Point(int a, int b) { 11 //PI=3.1415926; 12 /* 13 * 当使用静态常量的时候,不能在构造函数中初始化, 因为静态时,

C++中const关键字用法

为什么使用const?采用符号常量写出的代码更容易维护:指针常常是边读边移动,而不是边写边移动:许多函数参数是只读不写的.const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替),分类如下: 常变量:  const 类型说明符 变量名 常引用:  const 类型说明符 &引用名 常对象:  类名 const 对象名 常成员函数:  类名::fun(形参) const 常数组:  类型说明符 const 数组名[大小] 常指针:  const 类型说明符* 指针名 ,类型

C++中的const关键字的用法

1.const用于修饰普通变量,表示常量,不建议修改,某种程度上不允许修改(其实也是可以修改的) 指针常量 :指针(指向的变量的值)自身是一个常量,说明不能改变自身的指向  int* const p=&a;  常量指针 :指向常量的指针,指针可以修改指向 但是不能修改指向的变量的值  const int* p;面试必杀技: const出现在*的左边    是常量指针 const int num = 19; const int * p = &num;//指向常量的指针 常见的面试题中,经常会

C++中const关键字详解

1.什么是const? const意味着是常量类型,被const修饰的变量或对象是不能被修改和更新的,当然在某些情况下,我们可以偷梁换柱的改变它. 2.为什么要引入const? 最初的目的是为了取代预编译指令:define  MAX  (1024)  ,显然这样的宏定义常量是C语言中继承而来的,C++意识到这种宏替换会存在边际效应,因此希望const能替代它 3.const有什么作用? (1)可以定义const常量,具有不可变性. 例如: const int Max=100; int Array