C++类型限定符

138 阅读8分钟

C++类型限定符.png

类型限定符

const限定符

const变量:定义一种变量,值不能被改变,试图修改会报错

形式: const int i = 10;

基本类型和const

  • 基本类型的非constconst变相都能相互转化和赋值,和const无关

     int i = 42; 
     const int ci = i; //正确
     int j = ci; //正确
     bool bi = i; //正确,bi是true
    
  • 默认情况下,const对象仅在文件内有效

    若想跨文件访问,可以声明为extern

引用和const

  • const修饰的引用常量,不能通过引用修改它绑定的对象

     const int ci = 42;
     const int &r1 = ci; //正确,引用和其绑定对象都是常量
     ​
     r1 = 42; //错误,r1是对常量的引用
    
  • const的引用常量不能给非const变量赋值

    假设可以,那么久可以通过非const变量改变const引用的对象的值了,是不正确的

     int &r2 = ci; //错误
    
  • const变量可以给const引用常量赋值

     int i = 42;
     int &r1 = i; //正确
    

    虽然不能通过r1改变i的值,但是i自己还是可以改变的,i改变后,r1也就跟着变化了

     int i = 10;
     const int &ri = i;
     i = 20;
     ​
     > > 输出:i是20 , ri也是20
    

指针和const

  • 和引用一样,指针也可以指向常量或者非常量

    1. 可以用const常量给const指针赋值
    2. 也可以用非const变量给const指针赋值
    3. 不可用const常量给非const指针赋值
    4. const指针指向一个非const变量后,非const变量自身也是可以修改的
  • 与引用不同的是,指针是对象,引用不是

    所以存在指针本身是常量,称为常量指针,常量指针必须初始化,初始化后,就不能再改变

    1. const int *p = xx;

      p是一个指向常量的指针,p可变,*p不可变

    2. int * const p = xx;

      p是一个常量指针,p不可变,*p可变

    3. const int * const p = xx;

      p是一个指向常量的常量指针,p不可变,*p也不可变

    4. 理解const的含义,也可以从右向左理解 离p最近的有const,那么就是一个常量指针,没有就是指针变量 再往前,最开始有const,那么表示p指向的常量不可变;没有就表示指向的变量可变

顶层const和底层const

  • 顶层constconst修饰的变量不可变

     int i = 0;
     int *const p1 = &1;   //顶层const,p1不可变
     const int ci = 42; //顶层const,ci不可变
    

    顶层const在数据拷贝时不受影响

     const int *const p3 = p2;
     i = ci;  //ci是顶层const,无影响
     p2 = p3; //p3顶层const部分不受影响
    
  • 底层constconst修饰的指针变量指向的数据不可变

     const int *p2 = &ci; //底层const,p2指向的变量不可变
     const int &i = ci; //底层const,引用不可变
    

    底层const的限制不能被忽视。指向拷贝时,拷入和拷出的对象必须具有相同的底层const资格。

    一般的,非常量可以转换成常量,反之不行

     int *p = p3;  //错误,p3包含底层const,p不包含
     p2 = p3; //p2和p3都包含底层const
    

constexpr和常量表达式

  • 常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式

    字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式

     const int max_files - 20; //max_files是常量表达式
     ​
     const int limit = max_files + 1; //limit是常量表达式
     ​
     const int sz = get_size(); //sz不是常量表达式,因为具体值直到运行时才能获取到
    
  • 使用constexpr申明变量,以便编译器来验证变量是否是一个常量表达式

    声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化

     constexpr int mf = 20;  //20是常量表达式
     ​
     constexpr int limit = mf +1; //mf+1是常量表达式
     ​
     constexpr int sz = size(); //只有size是一个constexptr函数时才正确
    

字面值类型

  • 算数类型、引用和指针都属于字面值类型

  • 自定义的类都不属于字面值类型,不能定义成constexpr

  • 尽管指针和引用都能定义成constexpr,但初始值却受到严格限制

    一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象

  • 函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量

    定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针

指针和constexpr

  • constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:

     const int *p = nullptr; // p是一个指向整型常量的指针
     ​
     constexpr int *q = nullptr;  //q是一个指向整型的常量指针
    
  • 与其他常量指针类似,constexpr指针既可以指向常量也可以指向一个非常量

     int j = 0;
     constexpr int i = 42;
     ​
     constexpr const int *p = &i;   //指向常量,p是一个常量指针
     constexpr int *P1 = &j;  //指向非常量,p1也是常量指针
    

处理类型

类型别名

  • 形式

    • 传统的使用typedef

       typedef double wages;   //给double定义wages的别名
       wages *p = nullptr;  //使用别名定义指针数据
      
    • 新方式,使用别名声明using

      用关键字using作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定成等号右侧类型的别名

       using SI = double;  // SI是double的同义词
      
  • 指针、常量和类型别名

    • 类型别名使用在指针上,正好也加上了const限定,那么现在这个const指向的是什么?

       typedef char *pstring;   //pstring实际是char *的别名
       const pstring cstr = 0;   //cstr是指向char的常量指针,即cstr不可变
       const pstring *ps;   // ps是指向 char的常量指针 的指针
      

      pstring实际是char *的别名,而const是对给定类型的修饰。即const pstring就指向char的常量指针,而非指向char常量的指针

      也就说pstring是指针类型,那么const pstring中的const就是修饰指针的,也就是常量指针

    • 不能使用原始类型替换掉别名理解含义

       如 const char * cstr = 0;
      

      这是错误的

      pstring是指针类型。可是用char*重写了声明语句后,数据类型就变成了char成为了声明符的一部分。这样改写的结果是,const char成了基本数据类型。 前后两种声明含义截然不同,前者声明了一个指向char的常量指针,改写后的形式则声明了一个指向const char的指针

auto类型

自动类型,让编译器去分析表达式所属的类型

  • auto是让编译器通过初始值来推算变量类型,所以,auto定义的变量必须有初始值

     auto i = 0; //auto是int
    
  • 引用作为auto变量的初始值时,此时变量的类型就是引用对象的类型

     int i = 0 ; &r = i;
     auto a = r;  //a是int类型
    
  • 在同一行定义多个变量时

    符号&*只从属于某个声明符,而非基本数据类型的一部分,所以auto想要修饰引用或者指针也必须加上&或者*

     auto i = 0, &ri = i, *p = &i;
     //i是int 类型 
     //ri就是i的引用
     //p指向int类型i的数据
    
  • constauto

    • auto会忽略顶层const

      如果需要顶层const,则需要明确加上顶层const

       int i =0;
       const int ci = i; &cr  = ci;
       ​
       auto b = ci;  //b是一个int数据,顶层const被忽略
       const auto b2 = ci; //b2是一个int常量,明确机上顶层const
      
    • auto会保留底层const

       auto c = &ci;  //c是指向整型常量的指针 , 对常来那个取地址,是一种底层const
      

decltype类型指示符

decltype,它的作用是选择并返回操作数的数据类型 在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值

  • 形式

      decltype( f() )  sum = x; //sum的类型是函数f的返回值类型
    
  • auto的区别

    • decltype使用的表达式是一个变量,则类型就是该变量的类型,也会保留顶层const
    • decltype的结果类型与表达式形式密切相关
  • 总结

    • decltype( (variable) ) 永远是引用,注意,是两个括号

       int i = 0;
       decltype((i)) d = i;    //d是int&,必须初始化
      
    • decltype(引用) 是引用

       decltype(&i) d = i; //d是int&类型
      
    • decltype(*p) 是引用

       int p = &i;
       decltype(p) d = i;  //d是int &类型
      
    • decltype(引用作为表达式的一部分) 是引用的变量的数据类型

       decltype( i+0 ) d ;  //d是int类型
      
    • decltype(基本类型) 是数据类型

       decltype(i) d ; //d是int类型