const的思考

SAP Workflow审批功能实例详细过程 阅读详情
 

1、什么是const?

常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:)

2、为什么引入const?

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

3、cons有什么主要的作用?

①                  可以定义const常量,具有不可变性。例如:

const int Max=100;

int Array[Max];       

②                  便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。例如:

void f(const int i) { .........}

编译器就会知道i是一个常量,不允许修改;

③                  可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。

同宏定义一样,可以做到不变则已,一变都变!如①中,如果想修改Max的内容,只需要:const int Max=you want;即可!

④                  可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

还是上面的例子,如果在函数体内修改了i,编译器就会报错;

例如:void f(const int i) { i=10;//error! }

⑤                  为函数重载提供了一个参考。

class A

{

  ......

  void f(int i)       {......} file://一个函数

  void f(int i) const {......} file://上一个函数的重载

   ......

};

⑥                  可以节省空间,避免不必要的内存分配。例如:

#define PI 3.14159         file://常量宏

const doulbe  Pi=3.14159;  file://此时并未将Pi放入ROM中

......

double i=Pi;               file://此时为Pi分配内存,以后不再分配!

double I=PI;               file://编译期间进行宏替换,分配内存

double j=Pi;               file://没有内存分配

double J=PI;               file://再进行宏替换,又一次分配内存!

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

⑦                  提高了效率。

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

3、如何使用const?

①                  修饰一般常量

一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。例如:

int const x=2;  或  const int x=2;

②                  修饰常数组

定义或说明一个常数组可采用如下格式:

int const a[5]={1, 2, 3, 4, 5}; 

const int a[5]={1, 2, 3, 4, 5};

③                  修饰常对象

常对象是指对象常量,定义格式如下:class A;

     const A a;

     A const a;

定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,也可以放在类名前面。 

④                  修饰常指针

const int *A;        file://const修饰指向的对象,A可变,A指向的对象不可变

int const *A;       file://const修饰指向的对象,A可变,A指向的对象不可变

int *const A;       file://const修饰指针A,     A不可变,A指向的对象可变

const int *const A;  file://指针A和A指向的对象都不可变

⑤                  修饰常引用

使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:

const double & v;

⑥                  修饰函数的常参数

const修饰符也可以修饰函数的传递参数,格式如下:

void Fun(const int Var);

告诉编译器Var在函数体中的无法改变,从而防止了使用者的一些无意的或错误的修改。

⑦                  修饰函数的返回值:

const修饰符也可以修饰函数的返回值,是返回值不可被改变,格式如下:

    const int Fun1();

    const MyClass Fun2();

⑧                  修饰类的成员函数:

const修饰符也可以修饰类的成员函数,格式如下:

  class ClassName

 {

         public:

              int Fun() const;

                .....

 };

这样,在调用函数Fun时就不能修改类里面的数据

⑨                  在另一连接文件中引用const常量

extern const int i;     file://正确的引用

extern const int j=10;  file://错误!常量不可以被再次赋值

另外,还要注意,常量必须初始化!例如:

const int i=5; 

4、几点值得讨论的地方:

①                  const究竟意味着什么?

说了这么多,你认为const意味着什么?一种修饰符?接口抽象?一种新类型?

也许都是,在Stroustup最初引入这个关键字时,只是为对象放入ROM做出了一种可能,对于const对象,C++既允许对其进行静态初始化,也允许对他进行动态初始化。理想的const对象应该在其构造函数完成之前都是可写的,在析够函数执行开始后也都是可写的,换句话说,const对象具有从构造函数完成到析够函数执行之前的不变性,如果违反了这条规则,结果都是未定义的!虽然我们把const放入ROM中,但这并不能够保证const的任何形式的堕落,我们后面会给出具体的办法。无论const对象被放入ROM中,还是通过存储保护机制加以保护,都只能保证,对于用户而言这个对象没有改变。换句话说,废料收集器(我们以后会详细讨论,这就一笔带过)或数据库系统对一个const的修改怎没有任何问题。

②                  位元const V.S. 抽象const?

对于关键字const的解释有好几种方式,最常见的就是位元const 和 抽象const。下面我们看一个例子:

class A

{

 public:

       ......

       A f(const A& a);

       ......

 };

如果采用抽象const进行解释,那就是f函数不会去改变所引用对象的抽象值,如果采用位元const进行解释,那就成了f函数不会去改变所引用对象的任何位元。

我们可以看到位元解释正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员。

为什么这样呢?因为使用位元const有2个好处:

最大的好处是可以很容易地检测到违反位元const规定的事件:编译器只用去寻找有没有对数据成员的赋值就可以了。另外,如果我们采用了位元const,那么,对于一些比较简单的const对象,我们就可以把它安全的放入ROM中,对于一些程序而言,这无疑是一个很重要的优化方式。(关于优化处理,我们到时候专门进行讨论)

当然,位元const也有缺点,要不然,抽象const也就没有产生的必要了。

首先,位元const的抽象性比抽象const的级别更低!实际上,大家都知道,一个库接口的抽象性级别越低,使用这个库就越困难。

其次,使用位元const的库接口会暴露库的一些实现细节,而这往往会带来一些负面效应。所以,在库接口和程序实现细节上,我们都应该采用抽象const。

有时,我们可能希望对const做出一些其它的解释,那么,就要注意了,目前,大多数对const的解释都是类型不安全的,这里我们就不举例子了,你可以自己考虑一下,总之,我们尽量避免对const的重新解释。

③                  放在类内部的常量有什么限制?

看看下面这个例子:

class A

{

  private:

    const int c3 = 7;           // ???

static int c4 = 7;          // ???

static const float c5 = 7;  // ???

   ......

  };

你认为上面的3句对吗?呵呵,都不对!使用这种类内部的初始化语法的时候,常量必须是被一个常量表达式初始化的整型或枚举类型,而且必须是static和const形式。这显然是一个很严重的限制!

那么,我们的标准委员会为什么做这样的规定呢?一般来说,类在一个头文件中被声明,而头文件被包含到许多互相调用的单元去。但是,为了避免复杂的编译器规则,C++要求每一个对象只有一个单独的定义。如果C++允许在类内部定义一个和对象一样占据内存的实体的话,这种规则就被破坏了。

④                  如何初始化类内部的常量?

一种方法就是static 和 const 并用,在内部初始化,如上面的例子;

另一个很常见的方法就是初始化列表:

class A

{

 public:

       A(int i=0):test(i) {}

 private:

       const int i;

 };

 还有一种方式就是在外部初始化,例如:

class A

{

 public:

       A() {}

 private:

       static const int i;  file://注意必须是静态的!

 };

 const int A::i=3;

⑤                  常量与数组的组合有什么特殊吗?

我们给出下面的代码:

 const int size[3]={10,20,50};

 int array[size[2]];

 有什么问题吗?对了,编译通不过!为什么呢?

 const可以用于集合,但编译器不能把一个集合存放在它的符号表里,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储”。然而,其值在编译时不能被使用,因为编译器在编译时不需要知道存储的内容。自然,作为数组的大小就不行了:)

你再看看下面的例子:

 class A

 {

  public:

        A(int i=0):test[2]({1,2}) {} file://你认为行吗?

  private:

        const int test[2];

  };

vc6下编译通不过,为什么呢?

关于这个问题,前些时间,njboy问我是怎么回事?我反问他:“你认为呢?”他想了想,给出了一下解释,大家可以看看:我们知道编译器堆初始化列表的操作是在构造函数之内,显式调用可用代码之前,初始化的次序依据数据声明的次序。初始化时机应该没有什么问题,那么就只有是编译器对数组做了什么手脚!其实做什么手脚,我也不知道,我只好对他进行猜测:编译器搜索到test发现是一个非静态的数组,于是,为他分配内存空间,这里需要注意了,它应该是一下分配完,并非先分配test[0],然后利用初始化列表初始化,再分配test[1],这就导致数组的初始化实际上是赋值!然而,常量不允许赋值,所以无法通过。

呵呵,看了这一段冠冕堂皇的话,真让我笑死了!njboy别怪我揭你短呀:)我对此的解释是这样的:C++标准有一个规定,不允许无序对象在类内部初始化,数组显然是一个无序的,所以这样的初始化是错误的!对于他,只能在类的外部进行初始化,如果想让它通过,只需要声明为静态的,然后初始化。

这里我们看到,常量与数组的组合没有什么特殊!一切都是数组惹的祸!

⑥                  this指针是不是const类型的?

this指针是一个很重要的概念,那该如何理解她呢?也许这个话题太大了,那我们缩小一些:this指针是个什么类型的?这要看具体情况:如果在非const成员函数中,this指针只是一个类类型的;如果在const成员函数中,this指针是一个const类类型的;如果在volatile成员函数中,this指针就是一个volatile类类型的。

⑦                  const到底是不是一个重载的参考对象?

先看一下下面的例子:

class A

 {

   ......

   void f(int i)       {......} file://一个函数

   void f(int i) const {......} file://上一个函数的重载

    ......

  };

上面是重载是没有问题的了,那么下面的呢?

 class A

 {

   ......

   void f(int i)       {......} file://一个函数

   void f(const int i) {......} file://?????

    ......

 };

这个是错误的,编译通不过。那么是不是说明内部参数的const不予重载呢?再看下面的例子:

class A

{

  ......

  void f(int& )       {......} file://一个函数

  void f(const int& ) {......} file://?????

   ......

};

这个程序是正确的,看来上面的结论是错误的。为什么会这样呢?这要涉及到接口的透明度问题。按值传递时,对用户而言,这是透明的,用户不知道函数对形参做了什么手脚,在这种情况下进行重载是没有意义的,所以规定不能重载!当指针或引用被引入时,用户就会对函数的操作有了一定的了解,不再是透明的了,这时重载是有意义的,所以规定可以重载。

⑧                  什么情况下为const分配内存?

以下是我想到的可能情况,当然,有的编译器进行了优化,可能不分配内存。

A、作为非静态的类成员时;

B、用于集合时;

C、被取地址时;

D、在main函数体内部通过函数来获得值时;

E、const的 class或struct有用户定义的构造函数、析构函数或基类时;。

F、当const的长度比计算机字长还长时;

G、参数中的const;

H、使用了extern时。

不知道还有没有其他情况,欢迎高手指点:)       

⑨                  临时变量到底是不是常量?

很多情况下,编译器必须建立临时对象。像其他任何对象一样,它们需要存储空间而且必须被构造和删除。区别是我们从来看不到编译器负责决定它们的去留以及它们存在的细节。对于C++标准草案而言:临时对象自动地成为常量。因为我们通常接触不到临时对象,不能使用与之相关的信息,所以告诉临时对象做一些改变有可能会出错。当然,这与编译器有关,例如:vc6、vc7都对此作了扩展,所以,用临时对象做左值,编译器并没有报错。

⑩                  与static搭配会不会有问题?

假设有一个类:

class A

{

 public:

     ......

     static void f() const { ......}

     ......

 };

我们发现编译器会报错,因为在这种情况下static不能够与const共存!

为什么呢?因为static没有this指针,但是const修饰this指针,所以...

⑪                  如何修改常量?

有时候我们却不得不对类内的数据进行修改,但是我们的接口却被声明了const,那该怎么处理呢?我对这个问题的看法如下:

1)标准用法:mutable

   class A

   {

    public:

           A(int i=0):test(i)        { }

           void SetValue(int i)const { test=i; }

    private:

           mutable int test;   file://这里处理!

    };

2)强制转换:const_cast

    class A

    {

    public:

           A(int i=0):test(i)        { }

           void SetValue(int i)const

           { const_cast <int>(test)=i; }//这里处理!

    private:

           int test;  

    };

3)灵活的指针:int*

   class A

       {

        public:

               A(int i=0):test(i)        { }

               void SetValue(int i)const

               { *test=i; }

        private:

               int* test;   file://这里处理!

        };

4)未定义的处理

  class A

  {

   public:

          A(int i=0):test(i)        { }

          void SetValue(int i)const

          { int *p=(int*)&test; *p=i; }//这里处理!

   private:

          int test;  

   };

注意,这里虽然说可以这样修改,但结果是未定义的,避免使用!

5)内部处理:this指针

 class A

 {

  public:

         A(int i=0):test(i)        { }

         void SetValue(int i)const

         { ((A*)this)->test=i; }//这里处理!

  private:

         int test;  

  };

6)最另类的处理:空间布局

  class A

  {

   public:

         A(int i=0):test(i),c('a') {  }

   private:

         char c;

         const int test;

   };

   int main()

   {

        A a(3);

        A* pa=&a;

        char* p=(char*)pa;    

        int*  pi=(int*)(p+4);//利用边缘调整

        *pi=5;                 file://此处改变了test的值!

        return 0;

     }

虽然我给出了6中方法,但是我只是想说明如何更改,但出了第一种用法之外,另外5种用法,我们并不提倡,不要因为我这么写了,你就这么用,否则,我真是要误人子弟了:)

⑫                  最后我们来讨论一下常量对象的动态创建。

既然编译器可以动态初始化常量,就自然可以动态创建,例如:

const int* pi=new const int(10);

这里要注意2点:

1)const对象必须被初始化!所以(10)是不能够少的。

2)new返回的指针必须是const类型的。

那么我们可不可以动态创建一个数组呢?

答案是否定的,因为new内置类型的数组,不能被初始化。

这里我们忽视了数组是类类型的,同样对于类内部数组初始化我们也做出了这样的忽视,因为这涉及到数组的问题,我们以后再讨论。

【传感器模块项目实战】机器人/无人驾驶常用传感器模型、选型与安装 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!无人车&无人机导航合集本文先对机器人/无人驾驶常用传感器模型、选型与安装做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容分为位置式绝对型编码器增量式编码器打比赛的时候用过欧姆龙的增量式编码器,也用过待编码器的步进电机。【防盗标记–盒子君hzj】增量式编码器正交编码输出信号,用于识别正转和反转,通过微控制器(单片机)的片内外设硬件/软件解码(1)惯性器件-机械陀螺。 阅读详情

相关推荐

Rstan下载安装攻略

准备把Rtools删掉,重新下载(胡乱猜测是安装顺序的问题),但是卸载时显示Rtools被占用,但我已经把该关的都关了。这一问题的出现,直接让我卸载掉了Rstudio,又重新安装了一个,不过这次安装在另一个磁盘,因为原来的内存不多了。或者参考知乎,使用清华镜像网址进行下载:(知乎链接)https://zhuanlan.zhihu.com/p/346947595(清华下载链接)https://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/windows/Rtools/

m0_64643017的博客 3099

const的使用

const的使用

yanzhe1的博客 687

开源AI数字人DUIX:从零构建你的智能交互助手

本文详细介绍了如何利用开源AI数字人DUIX从零构建智能交互助手。文章阐述了DUIX作为开源项目的核心优势,包括低成本启动、低网络依赖和高度可定制性,并提供了从开发环境搭建、核心模块(AI模型、语音、形象)配置到个性化业务逻辑开发与最终部署的完整实践指南,帮助开发者快速掌握构建专属AI数字人的全流程。

weixin_29051811的博客 504

著名的《const的思考》

&lt;wbr&gt; 1、什么是const?&lt;br&gt; 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:)&lt;/wbr&gt; 2、为什么引入const?   const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。 3、cons有什么主要的作用? (1)可以定义const常...

wangxiaohigh 120

支付宝域名如何加入白名单(扫码老是弹窗)

支付宝扫码之后,遇到非支付宝官方网页,请确认是否继续访问弹窗,问题解决办法。本章教程提供解决办法,亲测有效。

猿小白的博客 9196

yolov8 转华为昇腾om脚本

yolov8 转华为昇腾om脚本

jacke121的专栏 1601

const用法以及一些思考

*const是一个关键字,用于声明一个变量为常量,这意味着一旦被赋值后,其值就不能被改变。在C++中,const**可以用来声明常量变量、常量指针、指向常量的指针等,以确保它们指向的值或它们本身在程序运行过程中不被修改。在C++中,常量可以通过**const**关键字声明和定义,以确保其值在程序执行期间不被修改。1. 基本类型的常量对于基本数据类型(如整数、浮点数等),可以直接在变量声明时使用**const**关键字来创建常量。2. 指针常量对于指针,**const。

u014634649的博客 1713

NSIS教程(8): 基于第三方界面库的安装包界面

前面的几篇文章介绍了NSIS的传统界面的安装包和现代界面的安装包的制作方法,也提到了NSIS支持自定义页面(即使用page custom)的特性,自定义页面需要用户自己创建对话框、控件、添加控件响应等等,虽然NSIS提供了nsDialogs.nsh来支持这些功能,但使用起来还是不太方便(需要专门了解这个插件诸多用法),而且不够灵活,所以本文介绍一种终极的自定义界面的安装包解决方案,即完全使用第三方

while(1) { smile(); } 9万+

关于const的思考

测试程序如下(在VC8的编译器下):class Test{public: int i;};void main(){ Test t;  //下面两种const的方式是一样的,都是限定Test这个类 const Test * * pp1;  //语句“(*pp1)->i = 100;”会产生编译错误 Test const* * pp2;  //语句“(*pp2)->i = 100;”会产生编译

学无止境 1181

Q3D提取PCB走线RLCG参数

介绍一种提取PCB走线寄生电容和寄生电感的方法

sinat_15150363的博客 2923

利用大模型和聚类算法找出 Excel 文件中重复或相似度高的数据,并使用 FastAPI 进行封装的详细方案

tokenizer将输入的文本列表texts转换为 PyTorch 张量,同时进行填充和截断操作,以确保所有输入具有相同的长度。对输入进行推理,得到模型的输出。对模型的最后一层隐藏状态求平均,得到每个文本的向量表示。.numpy()将 PyTorch 张量转换为 NumPy 数组。使用DBSCAN算法对文本向量进行聚类。eps是邻域半径,是形成核心点所需的最小样本数。fit方法对输入的向量进行聚类,并返回每个样本所属的簇标签。try:定义了一个 POST 请求的 API 路由,路径为。

AI的内部世界的博客 1069

const的思考 zz

 1、什么是const?   常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:)2、为什么引入const?  const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。3、cons有什么主要的作用?   (1)可以定义const常量,具有不可变性。        例如:            

482

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

对const的总结与思考

今天偶然想起const这一关键字,再加之以前几种const修饰指针方式让我印象深刻(混淆不清),重新回顾了一下,自己对这个关键词也又有了更加深刻的理解,所以总结一下。 一、const的定义 const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。 二、const的由来 最初的目的是为了取代预编译指令define,继承define的优点并且摈弃它的缺点。 举两个例子: ...

wei 344

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

const 思考

1、什么是const?   常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁换柱进行更新:)2、为什么引入const?  const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。3、cons有什么主要的作用?   (1)可以定义const常量,具有不可变性。        例如:             c

馋嘴猪没胃口 1319

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

上一篇: const 修饰符的认识理解与应用
下一篇: 深入理解C++中的mutable关键字
SearchLife
博客等级 码龄20年 51粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值