C++语言const 关键字使用方法图文详解

之前一直在学习C/C++,关于const的使用,这里出现一点,那里出现一点。知识用时方恨少,这一段时间正好各种笔试题,其中关于const的用法也是层出不穷,所以疲于在书本上各种翻,这里汇总一下,加深自己的印象的同时,也方便以后查阅和学习。菜鸟一个,若有错误,望指正!

const关键字

常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。不管出现在任何上下文都是为这个目的而服务的。

const使用方法

定义const对象

const修饰符可以把对象转变成常数对象,意思就是说利用const进行修饰的变量的值在程序的任意位置将不能再被修改,就如同常数一样使用!任何修改该变量的尝试都会导致编译错误:

注意:因为常量在定以后就不能被修改,所以定义时必须初始化:

对于类中的const成员变量必须通过初始化列表进行初始化,如下所示:

const对象默认为文件的局部变量

在全局作用域里定义非const变量时,它在整个程序中都可以访问,我们可以把一个非const变量定义在一个文件中,假设已经做了合适的声明,就可以在另外的文件中使用这个变量:

与其他变量不同,除非特别说明,在全局作用域声明的const变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定const变量为extern,就可以在整个程序中访问const对象。

注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。

const 引用

const引用是指向const对象的引用:

可以读取但不能修改refVal,因此,任何对refVal的赋值都是不合法的。这个限制有其意义:不能直接对ival赋值,因此不能通过使用refVal来修改ival。同理,用ival初始化ref2也是不合法的:ref2是普通的非const引用,因此可以用来修改ref2指向的对象的值。通过ref2对ival赋值会导致修改const对象的值,为防止这样的修改,需要规定将普通的引用绑定到const对象是不合法的。 const 引用可以初始化为不同类型的对象或者初始化为右值。如字面值常量:

同样的初始化对于非const引用却是不合法的,而且会导致编译时错误。其原因非常微妙,值得解释一下。观察将引用绑定到不同的类型时所发生的事情,最容易理解上述行为。对于下一段代码:

编译器会将这些代码转换为一下形式:

我们发现编译器会创建一个int型的暂时变量存储dval,然后将ri绑定到temp上。

注意:引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。

如果ri不是const,那么可以给ri赋一新值。这样做不会修改dval的,而是修改了temp。期望对ri赋值会修改dval的程序员会发现dval没有被修改。仅允许const引用绑定到需要临时使用的值完全避免了这个问题,直接告诉程序员不能修改,因为const引用是只读的哈~(其实就是避免程序员心理预期产生反差。。。)

注意:非const引用只能绑定到与该引用相同类型的对象。 const引用则可以绑定到不同但相关的类型的对象或绑定到右值。

const对象的动态数组

如果我们在自由存储区中创建的数组存储了内置类型的const对象,则必须为这个数组提供初始化: 因为数组元素都是const对象,无法赋值。实现这个要求的唯一方法是对数组做值初始化。

C++允许定义类类型的const数组,但该类类型必须提供默认构造函数:

这里便会调用string类的默认构造函数初始化数组元素。

指针和const限定符的关系(重点!!!!!!!非常容易搞混)

const限定符和指针结合起来常见的情况有以下几种。

指向常量的指针(指向const对象的指针)

C++为了保证不允许使用指针改变所指的const值这个特性,强制要求这个指针也必须具备const特性:

这里cptr是一个指向double类型const对象的指针,const先顶了cptr指向的对象的类型,而并非cptr本身,所以cptr本身并不是const。所以定义的时候并不需要对它进行初始,如果需要的话,允许给cptr重新赋值,让其指向另一个const对象。但不能通过cptr修改其所指对象的值。

而我们将一个const对象的地址赋给一个普通的非const指针也会导致编译错误。

不能使用void*指针保存const对象的地址,必须使用const void*类型的指针保存const对象的地址。

下面令人头晕的一个问题来了----à 允许把非const对象的地址赋给指向const对象的指针,例如:

但是我们不能通过cptr指针来修改dval的值!!!即使它指向的是非const对象。

然后,我们一定要知道,不能使用指向const对象的指针修改基础对象,然而如果该指针指向了非const对象,可用其他方式修改其所指的对象,所以事实上,可以修改const指针所指向的值的,但是不能通过const对象指针来进行而已!如下所示:

通过以上,我们知道指向const对象的指针 确切的讲: 自以为指向const的指针!

常指针(const指针)

C++中还提供了const指针——本身的值不能被修改。

我们可以从右往左把上述定义语句读作"指向int型对象的const指针"。与其他const量一样,const指针的值不能被修改,这意味着不能使curErr指向其他对象。Const指针也必须在定义的时候初始化。

指针本身是const的试试并没有说明是否能用改真真修改其所指向的对象的值。指针对象的值能否修改完全取决于该对象的类型。

指向常量的常指针(指向const对象的const指针)

如下可以这样定义:

这样pi_ptr首先是一个const指针,然后其指向一个const对象~~~

函数和const限定符的关系(另一难点!!!理解)

类中的const成员函数(常量成员函数)

在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:

<类型说明符> <函数名> (<参数表>) const;

其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。下面举一例子说明常成员函数的特征。

函数重载

既然const是定义为const函数的组成部分,那么就可以通过添加const实现函数重载咯。

其中print成员函数就实现了两个版本~~~ 重载哦,输出结果为 5,52。 const对象默认调用const成员函数。

const 修饰函数返回值

const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。如下所示:

一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const(const A& test = A实例) ,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

const修饰函数参数

传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)

参数指针所指内容为常量不可变

参数指针本身为常量不可变(也无意义,因为char* Var也是形参)

参数为引用,为了增加效率同时防止修改。修饰引用参数时:

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

const限定符和static的区别

const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间。
static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。
在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate=2.25; static关键字只能用于类定义体内部的声明中,定义时不能标示为static
在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。
const数据成员,只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。

const成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,但可以访问成员变量。当方法成员函数时,该函数只能是const成员函数。

static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有this指针,这导致:

1、不能直接存取类的非静态成员变量,调用非静态成员函数

2、不能被声明为virtual

其中关于static、const、static cosnt、const static成员的初始化问题:

类里的const成员初始化
在一个类里建立一个const时,不能给他初值

类里的const成员初始化

类中的static变量是属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此不能在定义对象时 对变量进行初始化,就是不能用构造函数进行初始化,其正确的初始化方法是:

数据类型 类名::静态数据成员名=值

类里的static cosnt 和 const static成员初始化(这两种写法是一致的!!)

最后通过一个完整的例子展示以上结果:

const 的难点

如果函数需要传入一个指针,面试官可能会问是否需要为该指针加上const,把const加在指针不同的位置有什么区别;如果写的函数需要传入的参数是一个复杂类型的实例,面试官可能会问传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。 const是用来声明一个常量的,当你不想让一个值被改变时就用const,const int max和int const max 是没有区别的,都可以。不涉及到指针const很好理解。一旦涉及到指针,则比较容易出问题。

如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向的对象为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a = 3 ;[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;[4]为指针本身和指向的内容均为常量。

如果理解了上面的所有信息,我相信应该问题不大。仅供参考~~~ 希望大家热烈讨论哈。 好了,先写到这里,在以后需要补充我再加东西吧。。。敲得手疼~~~ ~!!!

下面是其他网友的补充

在类中,如果你不希望某些数据被修改,可以使用const关键字加以限定。const 可以用来修饰成员变量、成员函数以及对象。

一、const 成员变量

const 成员变量的用法和普通 const 变量的用法相似,只需要在声明时加上 const 关键字。初始化 const 成员变量只有一种方法,就是通过参数初始化表

二、const 成员函数

const 成员函数可以使用类中的所有成员变量,但是不能修改成员变量,这种措施主要还是为了保护数据而设置的。const 成员函数也称为常成员函数。

常成员函数需要在声明和定义的时候在函数头部的结尾都加上 const 关键字,如:

class Student{
public:
  Student(char *name, int age, float score);
  void show();
  //声明常成员函数
  char *getname() const;
  int getage() const;
  float getscore() const;
private:
  char *m_name;
  int m_age;
  float m_score;
};

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(){
  cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
//定义常成员函数
char * Student::getname() const{
  return m_name;
}
int Student::getage() const{
  return m_age;
}
float Student::getscore() const{
  return m_score;
}

三.const对象

一旦将对象定义为const 对象之后,不管是哪种形式,则该对象就只能访问被 const 修饰的成员了(包括 const 成员变量和 const 成员函数),因为非 const 成员可能会修改对象的数据(编译器也会这样假设),C++禁止这样做。

例:

#include <iostream>
using namespace std;

class Student{
public:
  Student(char *name, int age, float score);
public:
  void show();
  char *getname() const;
  int getage() const;
  float getscore() const;
private:
  char *m_name;
  int m_age;
  float m_score;
};

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(){
  cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
char * Student::getname() const{
  return m_name;
}
int Student::getage() const{
  return m_age;
}
float Student::getscore() const{
  return m_score;
}

int main(){
  const Student stu("小明", 15, 90.6);
  //stu.show(); //error
  cout<<stu.getname()<<"的年龄是"<<stu.getage()<<",成绩是"<<stu.getscore()<<endl;

  const Student *pstu = new Student("李磊", 16, 80.5);
  //pstu -> show(); //error
  cout<<pstu->getname()<<"的年龄是"<<pstu->getage()<<",成绩是"<<pstu->getscore()<<endl;

  return 0;
}

好了这篇文章就介绍到这了,看完了这些基本上就可以理解了

(0)

相关推荐

  • C++中const、volatile、mutable使用方法小结

    相信const大家对他并不陌生,可能大家在日常的编写代码当中就会时常用到const,但是剩下的两个关键字不知道我们有 没有使用过volatile和mutable两个关键字其实不算特别常用,但是我们一定要知道这个关键字有什么用,应该怎么用.首 先const的基本操作我曾经写过一篇博客:const的基本使用 现在我要说一个const操作里面比较骚的一些做法, 举个例子我们以前写过的一个类,我们会使用operator[]来返回一个reference的指向,这个一般情况我们都会写一个const的也会写一

  • C++中const与#define的利弊分析

    C++中const与#define的区别如下: 用#define MAX 255定义的常量是没有类型的,所给出的是一个立即数,编译器只是把所定义的常量值与所定义的常量的名字联系起来,define所定义的宏变量在预处理的时候进行替换,在程序中使用到该常量的地方都要进行拷贝替换: 用const float MAX = 255; 定义的常量有类型名字,存放在内存的静态区域中,在程序运行过程中const变量只有一个拷贝,而#define 所定义的宏变量却有多个拷贝,所以宏定义在程序运行过程中所消耗的内存

  • C++ const关键字的实例用法

    C++中的const更像编译阶段的#define const int m = 10; int n = m; 变量是要占用内存的,即使被const修饰也不例外.m,n两个变量占用不同的内存,int n = m:表示将m的值赋给n. 在C语言中,编译器会先到m所在的内存取出一份数据,再将这份数据赋给n: 在C++中,编译器会直接将10赋给m,没有读取内存的过程,和int n = 10效果一样. 在C++中的常量更类似于#define命令,是一个值替换的过程,只不过#define是在预处理阶段替换,而

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

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

  • C++中const用于函数重载的示例代码

    常成员函数和非常成员函数之间的重载 首先先回忆一下常成员函数 声明:<类型标志符>函数名(参数表)const: 说明: (1)const是函数类型的一部分,在实现部分也要带该关键字. (2)const关键字可以用于对重载函数的区分. (3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数. (4)非常量对象也可以调用常成员函数,但是如果有重载的非常成员函数则会调用非常成员函数. 重载看例子: #include<iostream> u

  • C++语言const 关键字使用方法图文详解

    之前一直在学习C/C++,关于const的使用,这里出现一点,那里出现一点.知识用时方恨少,这一段时间正好各种笔试题,其中关于const的用法也是层出不穷,所以疲于在书本上各种翻,这里汇总一下,加深自己的印象的同时,也方便以后查阅和学习.菜鸟一个,若有错误,望指正! const关键字 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的.不管出现在任何上下文都是为这个目的而服务的. const使用方法 定义const对象 const修饰符可以把对象转变成常数对象,意

  • Android Studio 中运行 groovy 程序的方法图文详解

    Groovy简介 Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python.Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码.由于其运行在 JVM 上的特性,Groovy也可以使用其他非Java语言编写的库. Groovy 是 用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言.使用该种语言不必编写过多的代码,同时又具有闭包和动态语

  • Android Studio3.6.3 当前最新版本数据库查找与导出方法(图文详解)

    一.SQLite安装包准备 本文章主要是针对安装了Android Studio 3.6.3 版本(Android Studio以下简称为AS)所做的SQLite教程, 博主这边安装的是 , 由于SQL语言基本大同小异,仅仅是管理数据库的软件不大相同,所以说数据库使用方法类似,具体安装流程参考网络上其他文章,本文暂不提供安装流程,安装完毕即可. 二.重中之重----Android Studio 3.0 版本起弃用了之前使用的ADM 博主也是刚发现自AS 3.0 开始弃用 Android Devic

  • Kotlin 语言中调用 JavaScript 方法实例详解

    Kotlin 语言中调用 JavaScript 方法实例详解 Kotlin 已被设计为能够与 Java 平台轻松互操作.它将 Java 类视为 Kotlin 类,并且 Java 也将 Kotlin 类视为 Java 类.但是,JavaScript 是一种动态类型语言,这意味着它不会在编译期检查类型.你可以通过动态类型在 Kotlin 中自由地与 JavaScript 交流,但是如果你想要 Kotlin 类型系统的全部威力 ,你可以为 JavaScript 库创建 Kotlin 头文件. 内联 J

  • Mybatis调用Oracle存储过程的方法图文详解

    1:调用无参数的存储过程. 创建存储过程: Mapper.xml 配置:经测试其他标签(update.insert.select)也可以. Mapper.java MapperTest.java 测试 2:有参数的存储过程调用: 2.1存储过程的创建: 2.2Mapper.xml 的配置: 2.3Mapper.java 2.4MapperTest.java 测试 控制台输出: 3:存储过程的结果集调用. 3.1创建存储过程: 3.2 Mapper.xml 配置 配置 resultMap结果集字段

  • SSH原理及两种登录方法图文详解

    SSH(Secure Shell)是一套协议标准,可以用来实现两台机器之间的安全登录以及安全的数据传送,其保证数据安全的原理是非对称加密. 传统的对称加密使用的是一套秘钥,数据的加密以及解密用的都是这一套秘钥,可想而知所有的客户端以及服务端都需要保存这套秘钥,泄露的风险很高,而一旦秘钥便泄露便保证不了数据安全. 非对称加密解决的就是这个问题,它包含两套秘钥 - 公钥以及私钥,其中公钥用来加密,私钥用来解密,并且通过公钥计算不出私钥,因此私钥谨慎保存在服务端,而公钥可以随便传递,即使泄露也无风险.

  • Ubuntu16.04安装Jenkins的方法图文详解

    1.前提准备 JDK已经安装 echo $PATH 创建一个软连接 根据自己jdk安装路径来配置 ln -s /java/jdk1.8.0_231/bin/java /usr/bin/java 机器已经联网 卸载jenkins(如果已经安装就略过) sudo apt-get remove jenkins sudo apt-get purge jenkins sudo apt-get purge --auto-remove jenkins 2.通过如下命令完成安装 wget -q -O - htt

  • Pycharm 2019 破解激活方法图文详解

    使用破解补丁方法虽然麻烦,但是可用激活到2099年,基本上是永久激活了,毕竟在座各位能活到这个年份也是寥寥无几了吧!! 步骤一.下载破解补丁, 链接: https://pan.baidu.com/s/1ehsn5-DSXh2CZrc_mLOI5A 提取码: gwsn 并将破解补丁文件 JetbrainsCrack-release-enc.jar 放置到 pycharm 程序安装目录\bin目录下. 步骤二.在 Pycharm软件安装目录的\bin目录下找到 pycharm.exe.vmoptio

  • PyCharm License Activation激活码失效问题的解决方法(图文详解)

    自己的小Python项目好几天没有写了,今天打开PyCharm准备继续写,突然发现之前的激活码被取消不能用了,本来激情满满的准备干活啦!之前搜的这个激活码本来说的是可以用到18年1月份的呢,哎-心情是崩溃的-如下图所示: 在经过一顿搜索挣扎之后,笔者终于找到解决方法,血条瞬间满了,满血复活有没有,开开心心的开始敲代码: 解决方法是在下面的网站找到的,我只是个搬运工,来具体写一下步骤,解决一下网站中网友的问题,网址如下:http://idea.lanyus.com/ 很多网友提问说,用了这里的激活

  • VSCode各语言运行环境配置方法示例详解

    系统环境变量的配置 如:将F:\mingw64\bin添加到系统环境变量Path中 VSCode软件语言json配置C语言 创建个.vscode文件夹,文件夹内创建以下两个文件 launch.json 文件配置 { "version": "0.2.0", "configurations": [ { "name": "(gdb) Launch", "type": "cppdbg&

随机推荐