详解C/C++中const关键字的用法及其与宏常量的比较

1.const关键字的性质

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

2.修饰局部变量

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

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

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

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

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

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

3.修饰指针

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

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

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

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

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

上面的写法报错

 int c = 3;
 const int *n = &a;
 a = 10;
 a = c;

这样赋值是可以的。

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

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

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

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

而这么写是错误的

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

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

指向常量的常指针

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

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

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

4.修饰引用

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

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

5.函数中使用const

(1)修饰函数参数

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

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

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

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

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

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

另外当参数为引用时

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

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

(2)修饰函数返回值

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

const int * fun2() //调用时 const int *pValue = fun2();
       //我们可以把fun2()看作成一个变量,即指针内容不可变。
 c.int* const fun3() //调用时 int * const pValue = fun2();
       //我们可以把fun2()看作成一个变量,即指针本身不可变。
const int fun1() //这个其实无意义,因为参数返回本身就是赋值。

6.修饰类相关

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

class A
{
public:
 A(int x) : a(x) // 正确
 {
   //a = x; // 错误
 }
private:
 const int a;
};

(2)const修饰成员函数

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

class A
{
public:
 int& getValue() const
 {
  // a = 10; // 错误
  return a;
 }
private:
 int a;   // 非const成员变量
};

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

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

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

用const修饰的类对象表示该对象为常量对象,该对象内的任何成员变量都不能被修改。对于对象指针和对象引用也是一样。

因此不能调用该对象的任何非const成员函数,因为对非const成员函数的调用会有修改成员变量的企图。

class A
{
 public:
 void funcA() {}
 void funcB() const {}
};
int main
{
 const A a;
 a.funcB(); // 正确
 a.funcA(); // X

 const A* b = new A();
 b->funcB(); // 正确
 b->funcA(); // X
}

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

class A
 {
 public:
  void func() {}
  void func() const {} // 重载
 };

另外,const数据成员只在某个对象生存期内是常量,而对整个类而言是可变的,因为类可以创建多个对象,不同对象的const数据成员值可以不同。

class A
{
public:
 A(int size)
   : _size(size) // 正确
 {}
private:
 const int _size;
};
A a(10); //对象a的_size值为10
A b(20); //对象b的_size值为20

那么,怎样才能建立在整个类中都恒定的常量呢?用枚举常量。

class A
{
public:
 enum{SIZE1 = 10, SIZE2 = 20};//枚举常量
private:
  int arr1[SIZE1];
  int arr2[SIZE2];
};

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。但缺点是隐含数据类型是只能整数,最大值有限,且不能表示浮点数。

7.修饰全局变量

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

8.const常量与宏常量的区别

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

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

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

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

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

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

(3)提高了效率 

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

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

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

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

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

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

 class A
 {
   ......
 void f(int i)  {......} //一个函数
 void f(int i) const {......} //上一个函数的重载
   ......
 };

(6)定义域不同

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

(7)做函数参数

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

9.const_cast

const_cast运算符用来修改类型的const或volatile属性。

(1)常量指针被转化成非常量的指针,并且仍然指向原来的对象;

(2)常量引用被转换成非常量的引用,并且仍然指向原来的对象。

 void func()
 {
  const int a = 10;
  int* p = const_cast<int*> (&a);
  *p = 20;
  std::cout<<*p; // 20
  std::cout<<a;  // 10
 }

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

以上所述是小编给大家介绍的C/C++中const关键字的用法及其与宏常量的比较,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 浅析C++的引用与const指针与各种传递方式

    浅析C++的引用与const指针与各种传递方式 首先我们知道 const int *p 与 int const *p 是一样的,即 *p 是常量:而 int * const p 跟上面是不一样的,即 p 是常量:我们知道引用只是一个别名,与变量共享存储空间,并且必须在定义的时候初始化,而且不能再成为别的变量的别名,这让我们想到什么呢,貌似跟  int * const p   的性质很像. 其实引用的底层就是用const指针来实现的.下面举个小例子: #include <iostream> us

  • C++中的const的使用详解

     C++中的const的使用详解 const在c/c++中还是会经常出现的,并且如果不理解const会在编程出现的错误而不知所措,无法理解.下面从几个角度简要理解const的内容,应该还是蛮有用的. const与指针类型 const int*p = NULL; 和int const*p = NULL;是等价的.因为const都在" * "的前面,其实是以*为标志的. 1. int x = 3; const int *p = &x; // p = &y;正确 , //*p

  • C++ const引用、临时变量 引用参数详解

    C++引用-临时变量.引用参数和const引用 如果实参与引用参数不匹配,C++将生成临时变量.如果引用参数是const,则编译器在下面两种情况下生成临时变量: 实参类型是正确的,但不是左值 实参类型不正确,但可以转换为正确的类型 左值参数是可被引用的数据对象,例如,变量.数组元素.结构成员.引用和被解除引用的指针都是左值,非左值包括字面常量和包含多项式的表达式.定义一个函数 Double refcube(const double& ra) { Returnra*ra*ra; } double

  • C/C++中CONST用法总结(推荐)

    1.修饰常量时: const int temp1; //temp1为常量,不可变 int const temp2; //temp2为常量,不可变 2.修饰指针时: 主要看const在*的前后,在前则指针指向的内容为常量,在后则指针本身为常量: const int *ptr; //*ptr为常量: int const *ptr; //*ptr为常量: int* const ptr; //ptr为常量: const int * const ptr; //*ptr.ptr均为常量: 3.const修饰

  • C++ 中const对象与const成员函数的实例详解

    C++ 中const对象与const成员函数的实例详解 const对象只能调用const成员函数: #include<iostream> using namespace std; class A { public: void fun()const { cout<<"const 成员函数!"<<endl; } void fun() { cout<<"非const成员函数 !"<<endl; } }; int

  • C++ 中 const和static readonly区别

    C++ 中 const和static readonly区别 我们都知道,const和static readonly的确很像:通过类名而不是对象名进行访问,在程序中只读等等. 在多数情况下可以混用. 二者本质的区别在于,const的值是在编译期间确定的,因此只能在声明时通过常量表达式指定其值.而static readonly是在运行时计算出其值的,所以还可以通过静态构造函数来赋值. 明白了这个本质区别,我们就不难看出下面的语句中static readonly和const能否互换了: 1. stat

  • 浅析c++ 中const关键字

    const是一个C++语言的限定符,它限定一个变量不允许被改变.使用const在一定程度上可以提高程序的安全性和可靠性.另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助. define与const的区别 1.define作用在预处理时,是简单地字符替换 2. const作用在编译时,具有类型检查的功能 3. const必须进行初始化 常量指针与指针常量 #include <iostream> using std::endl; using std::cout

  • C++ 中const修饰虚函数实例详解

    C++ 中const修饰虚函数实例详解 [1]程序1 #include <iostream> using namespace std; class Base { public: virtual void print() const = 0; }; class Test : public Base { public: void print(); }; void Test::print() { cout << "Test::print()" << end

  • 详解C/C++中const关键字的用法及其与宏常量的比较

    1.const关键字的性质 简单来说:const关键字修饰的变量具有常属性. 即它所修饰的变量不能被修改. 2.修饰局部变量 const int a = 10; int const b = 20; 这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了,而且编译器也不允许不赋初值的写法: 在C++中不赋初值的表达一写出来,编译器即报错,且编译不通过. 在C中不赋初值的表达写出来时不报错,编译时只有警告,编译可以通过.而

  • 详解C语言中const关键字的用法

    关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点: 1.预编译指令只是对值进行简单的替换,不能进行类型检查 2.可以保护被修饰的东西,防止意外修改,增强程序的健壮性 3.编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编

  • 详解Java编程中super关键字的用法

    通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量.但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的. 好了,现在开始讨论this&super这两个关键字的意义和用法.在Java中,this通常指当前对象,super则指父类的.当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当

  • C/C++ 中const关键字的用法小结

    C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性. Const作用 NO. 作用 说明 参考 1 可以定义const常量 const int Max = 100; 2 便于进行类型检查 const常量有数据类型,而宏常量没有数据类型.编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误 void f(const int i) { ---} //对传入的参数进行类型检查,不匹配进行提示 3 可以保护被修

  • 详解Shell 脚本中 “$” 符号的多种用法

    通常情况下,在工作中用的最多的有如下几项: $0:Shell 的命令本身 1到9:表示 Shell 的第几个参数 $? :显示最后命令的执行情况 $#:传递到脚本的参数个数 $$:脚本运行的当前进程 ID 号 $*:以一个单字符串显示所有向脚本传递的参数 $!:后台运行的最后一个进程的 ID 号 $-:显示 Shell 使用的当前选项 ...... 今天将通过以上几种选项并做进一步的操作案例: 1.引用变量 引用变量时,使用 $ 符号直接来进行引用,以及包括循环变量: [root@localho

  • 实例详解C/C++中extern关键字

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是f

  • 详解C/C++中const限定符总结

    const限定符 const是一种限定符,被const所限定的变量其值不可以被改变. const的初始化 由于const一旦创建其值就不能够被改变,所以我们必须对其进行初始化 const int a;//错误,const变量必须进行初始化! const int b=10;//正确,编译时初始化 const int c=get_size();//正确,运行时初始化 相同类型的变量相互初始化时,不论变量是否被const限定我们都可以进行随意的相互拷贝.因为在拷贝过程中我们只会用到等式右边变量的右值属

  • 详解WordPress开发中wp_title()函数的用法

    wp_title 函数在 WordPress 中是用来显示文章.页面.分类等等等等标题的一个函数,但在首页索引,该函数将不显示任何的东西.该函数在 WordPress 官方主题中一直被使用,但目前很多定制的主题中这个函数总是为忽视. 函数意义详解 wp_title 函数用来显示页面的标题,如在文章页面,则显示文章标题:在分类页面,则显示分类名称,但在首页索引,该函数将不显示任何的东西. 有点像 WordPress 中的 get_the_title 和 single_cat_title()这两个函

  • 详解SqlServer数据库中Substring函数的用法

    功能:返回字符.二进制.文本或图像表达式的一部分 语法:SUBSTRING ( expression, start, length ) 1.substring(操作的字符串,开始截取的位置,返回的字符个数) 例如: 从'abbccc'中返回'ccc',charindex函数用法(charindex(查找的字符串,被查找的字符串,开始查找的位置),例如查找'abbccc'中第一个'c'出现的位置,charindex('c','abbccc',1)) declare @str1 varchar(25

  • 详解Java编程中static关键字和final关键字的使用

    Java static关键字以及Java静态变量和静态方法 static 修饰符能够与变量.方法一起使用,表示是"静态"的. 静态变量和静态方法能够通过类名来访问,不需要创建一个类的对象来访问该类的静态成员,所以static修饰的成员又称作类变量和类方法.静态变量与实例变量不同,实例变量总是通过对象来访问,因为它们的值在对象和对象之间有所不同. 请看下面的例子: public class Demo { static int i = 10; int j; Demo() { this.j

随机推荐