概览C++之const

1、C语言中const与 C++中的const

void main()
{
  const int a = 10;
  int *p = (int*)&a;
  *p = 20;
  printf("%d", a);
}

比較上述代码在C中和C++中执行结果:C:打印20;C++:打印0。

由此可见,C语言中的const是个“冒牌货”,C++中
的const是一个真正的常量。

原因:C语言直接为a分配了内存。

C语言中的const变量是仅仅读变量,有自己的存储空间。而C++编译器对const的处理是:当碰见常量声明时,在“符号表”(C++编译器专门设置的)中放入常量,在使用a的时候直接在符号表中取值。
那么问题是那又怎样解释取地址呢?

A.编译过程中若发现使用常量则直接以符号表中的值替换;

B.编译过程中若发现对const使用了extern(须要在其它文件里使用)
或者 &操作符,则给const常量另外再专门分配存储空间。

如上图所看到的,对const变量取地址后,为“20”单独专门分配了空间。而与符号表中的a没有关系。

2、const与基础数据类型:

   const int bufsize = 512。bufsize的值不能再被改动。不论什么试图为bufsize赋值的行为都将引发错误。

由于const的对象一旦创建后其值不能再改变,所以const对象必须初始化。
   int i=12;const int ci=i;还能够利用另外一个对象(是不是const无关紧要)去初始化const对象。。

。int j = ci。一旦拷贝完毕,新的对象和原来的对象就没有什么关系了。
{
   int i = 50;
   const int j = i;      //初始化
   int k = j;
   int m = 100;
   //j = m;              //赋值,编译错误
}

默认状态下,const对象仅在文件内有效。当多个文件里出现了同名的const对象,事实上等同于在不同文件定义了独立的变量。

假设const对象须要在不同文件共享,那就须要对const对象无论是声明还是定义都加入externkeyword。

注意仅仅能定义一次:extern const int bufsize = 512。一般被包括在头文件。

在使用时直接声明:extern const int bufsize;不能再定义,否则出错。

extern就说明了bufsize并非本文件独有,它的定义在别处。

3、const与引用

void main()
{
   //对常量对象 只能 用 常量引用
   const int ci = 100;  //常量对象
   const int &ri = ci;  //正确:引用及其相应的对象都是常量
   //ri = 100;          //error:ri是对常量的引用
   //int &r2 = ci;      //error:将一个很量引用指向一个常量对象,假设正确,那么将能够通过改动r2来改变ci的值,ci显然不能改变。

   //常量引用 同意被 绑定到 很量的对象、 字面值、 一般表达式 ————例外1
   int i = 50;
   const int &CRi1 = i;        //很量的对象
   const int &CRi2 = 500;      //字面值
   const int &CRi3 = i * 2;    //一般表达式
   int r4 = CRi1 * 2;          //正确:CRi1的常量特征只在运行改变的时候才会发生作用,拷贝赋值并不会改变。一旦拷贝完毕,新的对象和原来的对象就没有什么关系了。
   //int &r5 = CRi1 * 2;        //error:将一个很量引用指向一个常量对象。假设正确。那么将能够通过改动r5来改变CRi1的值,CRi1显然不能改变。

/* * * * * * * * *
             *一般来说。引用的类型必须与其所引用对象的类型一致。
             *当一个常量引用被绑定到第二种类型上究竟发生了什么?
             *   double d = 3.14;
                 const int &ri = d;

             *   const int tmp = d;   // 由双精度浮点数生成一个暂时的整型常量
                 const int &ri = d;   // 让ri绑定这个暂时的常量
             * * * * * * * * */

   //常量引用只对自己可參与的操作做了限定,假设引用的对象是个很量,那么同意通过其它途径改变它的值
   int j = 10;
   int &rj = j;             //引用rj 绑定 对象j
   const int &cj = j;       //常量引用cj 也绑定 对象j。可是不同意通过cj 去改动j的值
   rj = 1000;               //通过rj 改动j,j被改动为1000
   //cj = 1000;
   cout << "j: " << j << "  ,cj: " << cj << endl;   //j: 1000  ,cj: 1000
}  

const引用传递对象(非内置类型):

(1)效率高:没有不论什么新的对象被创建,所以也就不会调用不论什么的构造函数和析构函数。(内置类型值传递方式效率高)

(2)能够避免对象被分割:假设派生类对象以值传递方式赋值给基类。派生类的特化性质会被所有分割,只留下一个base类对象。

4、const与指针

所谓指向常量 的指针或者引用。只是是指针或引用“自以为是”罢了,它们自己认为自己指向了常量,所以自觉地不去改变它所指向的对象的值。(可是常量 的指针或者引用 是能够 指向很量的,而很量是能够通过其它途径改变的)。

指针是对象,而引用不是!因此。指针本身是不是常量 和 指针所指的是不是一个常量 是两个相互独立的问题!

顶层const——指针本身是个常量!

底层const——指针所指的对象是个常量。

void main()
{
    int i = 10, m = 20;
    const int ci = 100, cm = 200;

    //第一、二个意思一样,代表一个  常整形数,不论什么试图为a、b赋值的行为都将引发错误
    const int a = 0;        //必须初始化
    int const b = 1;        

    //第三个 c是一个指向常整形数的指针(所指向的内存数据不能被改动,可是本身能够改动)
    const int *c1 = &ci;
    //int *c2 = &ci;        //存放常量的地址,仅仅能使用指向常量的指针

    const int *c = &i;
    //*c = m;               //c指向的内存数据是const的(c的自我感觉),也就是不能通过 *c “直接改动”的
    c = &m;                 //c的值是同意改动的(底层const)。改动c的指向。让c指向另外一个内存空间
    cout << *c << endl;     //20

    //第四个 d 常指针————指针本身是个常量(指针变量(指向)不能被改动,可是它所指向内存空间能够被改动)
    int ii = 0;
    int * const d = ?    //d 将一直指向 ii (顶层const),不同意改动 d 的值
    *d = 250;               //指针本身是个常量,并不意味着不能通过改动指针所指向的值。

(依赖于所指向的对象)
    cout << ii << endl;     // ii 被改动成250。(d指向的是一个很量)

    //第五个 e 一个指向常整形的常指针(指针和它所指向的内存空间,均不能被改动)
    const int pi = 123;
    const int * const e = π   //e所指的对象值 和 e自己存储的那个地址 都不能改变
}

5、const与成员函数

将const实施与成员函数的目的,是为了确认该成员函数可作用于const对象身上(非const成员函数本来就能够对其对象做不论什么操作,所以在当中调用一个const成员函数也正确)。

两个函数假设仅仅是常量性(一个是const成员函数,一个是非const成员函数)不同。也能够被重载。作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不正确数据成员进行改动操作,应尽可能将该成员函数声明为const 成员函数。在C++中。仅仅有被声明为const的成员函数才干被一个const类对象调用。把一个成员函数声明为const能够保证这个成员函数不改动数据成员。可是,假设据成员是指针,则const成员函数并不能保证不改动指针指向的对象,编译器不会把这样的改动检測为错误。

int foo(int *test) const;//可看做:int foo(const A *this,int *test);

6、const与define

const修饰的常量编译期间,就确定下来。const常量是由编译器处理的,提供类型检查和作用域检查;宏定义由预处理器处理。单纯的文本替换。

void fun1()
{
    #define a 10
    const int b = 20;
    //#undef a  //将宏定义a卸载,限制作用域
}

void fun2()
{
    printf("a = %d\n", a);
    //printf("b = %d\n", b);   //const定义的b是有作用域的
}
 

7、const与constexpr

constexpr:常量表达式,指的是值不会改变而且在编译过程中就能得到计算结果的表达式。

由数据类型和初始值共同决定。

   const int max = 100;         //max是常量表达式
   const int limit = max + 1;   //limit是常量表达式
   int size = 50;               //size不是常量表达式,仅仅一个普通的int类型
   const int sz = get_size();   //sz本身是个常量,但它的值直到执行时才干确定,所以不是常量表达式

   一般说来,假设你认定变量是一个常量表达式。那就把它声明成constexpr类型。

const int sz = get_size();   //仅仅有get_size()是一个constexptr函数时。才是一条正确的声明语句。

8、const与auto

auto通常会忽略掉顶层const。同一时候底层const则会保留。

void main()
{
   //auto通常会忽略掉顶层const,同一时候底层const则会保留。

int i = 10;
   const int ci = i;
   const int &cr = ci;
   auto b = ci;           //b是一个int(ci的顶层const特性被忽略)
   auto c = cr;           //c是一个int(cr是ci的别名,ci的顶层const特性被忽略)
   auto d = &i;           //d是一个整型指针(整数的地址就是指向整数的指针)
   auto e = &ci;          //e是一个指向整型常量的指针(对常量对象 取地址 是一种底层const)

   //假设希望判断出的auto类型是一个顶层const。则需明白指出
   const auto f = ci;     //ci的推演类型是int,f是const

   //还能够将引用设为auto,原来的初始化规则仍然适用
   auto &g = ci;          //g是一个常量引用。绑定到ci
   //auto &h = 42;        //error:显然不能为一个字面值绑定很量引用。

(怎么说?h是42的引用?字面量42没有内存空间 没有办法做引用)
   const auto &j = 42;    //正确:能够为常量引用绑定字面值
}

9、const与decltype

decltype处理顶层const和引用的方式与auto不同。假设decltype使用的表达式是一个变量,则decltype返回该变量的类型。

  int i = 10;
const int ci = i;
const int &cr = ci;
decltype(ci) x = 0;    //x的类型是const int
decltype(cr) y = x;    //y的类型是const int&。y绑定到x
//decltype(cr) z;      //error:z是一个引用,必须初始化
 

10、const与迭代器

vector<int> vec;
const vector<int>::iterator ite = vec.begin();         //ite 相当于T×  const
*ite = 10;                                                                    //正确,改变ite所指之物
++ite;                                                                          //错误,ite是const的

vector<int>::const_iterator conIte = vec.begin(); //conIte相当于 const  T×
*conIte = 10;                                                             //错误,×conIte是const的
++conIte;                                                                   //正确,改变conIte
时间: 2024-08-07 00:15:53

概览C++之const的相关文章

Qt事件机制概览

Qt事件机制概览 Qt事件机制概览 消息循环 Qt事件循环 简介 QEventLoop 跨线程的信号和槽与事件循环 模态窗口 Native widget or Alien widget 创建Native widget 创建QApplication的message-only窗口 派发事件的公共基础方法 source code QApplication的创建过程 QWidget native QWidget 的创建过程 普通native widget回调过程 QApplication的message

OSG开发概览(转载)

OSG开发概览 1 OSG基础知识 Ø OSG是Open Scene Graphic 的缩写,OSG于1997年诞生于以为滑翔机爱好者之手,Don burns  为了对滑翔机的飞行进行模拟,对openGL的库进行了封装,osg的雏形就这样诞生了,1998年Don burns 遇到了同样喜欢滑翔机和计算机图形学的Robert Osfield ,从此Robert Osfield加入了osg小组的开发并一直担任开发小组的组长. Ø OSG不但有openGL的跨平台的特性和较高的渲染性能,还提供了一系列

IOS开发系列&mdash;Objective-C之基础概览

概览 前面我们已经用了几章内容进行C语言介绍,当然要通过几篇文章完整的介绍C语言的知识是不太现实的,例如C语言的文件操作.内存申请等我们都没有重点介绍,当然核心知识点基本都已经提到了,后面有时间我们会继续扩充.今天我们正式开始学习Objective-C,以后简称"ObjC",ObjC是在C语言的基础上加上了一层面向对象的特性,它完全兼容C语言,甚至可以混写C++.它是Mac OS X和IOS的主要开发语言,从IOS发布之后可以说ObjC的地位呈直线上升趋势,当前ObjC已经是仅次于C语

OpenGL ES着色器语言之着色概览(官方文档)

OpenGL ES着色器语言之着色概览(官方文档第二章) 事实上,OpenGL ES着色语言是两种紧密关联的语言.这些语言用来在OpenGL ES处理管线的可编程处理器创建着色器. 在本文档中,除非另外说明,一个语言功能适用于所有语言,并且通用用法将把他们当做一个语言来看待.特定语言将指出它们的目标处理器:顶点(vertext)或片元(fragment). 任何被着色器使用的OpenGL ES状态值都会自动地被跟踪并且作用于着色器上.这个自动状态跟踪机制允许应用程序为状态管理而使用OpenGL

dex2oat将dex转换为oat的执行路径概览

dex2oat是一个可执行文件,在源码中通过编译文件art\dex2oat\Dex2oat.cc生成. dex2oat的执行入口是: int main(int argc, char** argv) { return art::dex2oat(argc, argv); } 在函数dex2oat中调用了dex2oat->CreateOatFile函数,在CreateOatFile函数中执行了driver->CompileAll(class_loader, dex_files, timings);语

你不知道的JavaScript--Item24 ES6新特性概览

ES6新特性概览 本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony(和谐之意,显然没有跟上我国的步伐,我们已经进入中国梦版本了).上一次标准的制订还是2009年出台的ES5.目前ES6的标准化工作正在进行中,预计会在14年12月份放出正式敲定的版本.但大部分标准已经就绪,且各浏览器对ES6的支持也正在实现中.要查看ES6的支持情况请点此. 目前想要

iOS开发系列—Objective-C之基础概览oc格式化输出例子

概览 前面我们已经用了几章内容进行C语言介绍,当然要通过几篇文章完整的介绍C语言的知识是不太现实的,例如C语言的文件操作.内存申请等我们都没有重点介绍,当然核心知识点基本都已经提到了,后面有时间我们会继续扩充.今天我们正式开始学习Objective-C,以后简称"ObjC",ObjC是在C语言的基础上加上了一层面向对象的特性,它完全兼容C语言,甚至可以混写C++.它是Mac OS X和IOS的主要开发语言,从IOS发布之后可以说ObjC的地位呈直线上升趋势,当前ObjC已经是仅次于C语

开源跨平台数据格式化框架概览

说到数据格式化框架,就不得不提到 Google 的 Protocol Buffers,Facebook 的 Thrift,还有 Apache Hadoop 推出的 Avro.Microsoft 最近开源的 Bond 也是一种用于数据格式化的可扩展框架,其适用的应用场景包括服务间通信.大数据存储和处理等. 为什么会有这么多关于数据格式处理的框架?它们都在解决什么问题呢?我们先来观察一下典型的服务间通信的结构. 通常,在设计服务间通信时,我们所要面对的基本问题有: 如何传输数据? 使用什么协议通信?

白话ASP.NET MVC之二:Controller激活系统的概览

前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了.我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了.如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是