菜鸟的天花板

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  23 随笔 :: 0 文章 :: 0 评论 :: 2138 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
const对象一旦创建后其值就不能再被改变,所以const对象必须初始化。主要的限制是只能在const类型的对象上执行不改变其内容的操作。
注:默认状态下,const对象仅在文件内有效。当多个文件出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
注:“const int max; ” 与  “int const max;” 没有区别。
 
(1)“const"放在函数名后面,声明该类成员函数,以表明它们不能修改类中数据成员的值。这类带“const”的函数又被称为常量成员函数。
 
(2)以下两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式。
                    const int r[ ]={1,2,3,4};
                    struct S {int a,b;};
                    const S s[ ]={(1,2),(3.4)};
(3)对于字符数组,如char *name = "China";这样的语句,在编译时是能够通过的,但是因为 "China"是常量字符数组,任何想修改他的操作也能通过编译
    但会引起运行时错误,如果想修改字符数组的话,就要使用char name[] = "China";这种形式。
 
(4)对于函数
        1)void Func(const int r);
        此处为参数传递C++ const变量值,意义是变量初值不能被函数改变。
        2)const int Func(int);
        此处返回const值,意思指返回到调用函数里的变量的初值不能被修改。对于用户自定义类型,返回值是常量是非常重要的,如下:
                    Class CX;            // 内部有构造函数,声明如CX(int r = 0)
                    CX Func1() {return CX();}
                    const CX Func2() {return CX();}
        对上面的自定义类CX,和函数Func1()和Func2(),进行如下操作时:
                    Func1() = CX(1);    // 没有问题,可以作为左值调用
                    Func2() = CX(1);    // 编译错误,const返回值禁止作为左值调用
        3)函数中指针的const变量传递和返回:
                    int F1(const char *pstr);
        作为传递的时候使用const修饰,可以保证不会通过这个指针来修改传递参数的初值,即在函数内部任何修改*pstr的企图都会引起编译错误。
                    const char *F2();      
        意义是函数返回的指针指向的对象是一个const对象,它必须赋给一个同样是指向const对象的指针。
                    const char * const F3();
        比上面多了一个const,这个const的意义只是在它被用作左值时有效,它表明了这个指针除了指向const对象外,它本身也不能被修改,即常量指针。
        4)函数中引用的const传递:
                    void F1(const X &px);
        这样的一个C++ const变量引用传递和最普通的函数按值传递的效果是一模一样的,它禁止对引用的对象的一切修改,唯一不同的是按值传递
        会先建立一个类对象的副本,然后传递过去,而引用直接传递地址,所以这种传递比按值传递更有效。
注:只有引用的const传递可以传递一个临时对象,因为临时对象都是带const属性的,且是不可见的,它短暂地存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到它。
 
(5)对于类
        1)首先,对于const 成员变量,只能在构造函数里使用初始化成员列表来初始化,试图在构造函数体内进行const成员变量的初始化会引起编译错误。
      初始化成员列表形如:
                    X::X(int r):r(ir) {}
           假设r是类X的C++ const成员变量。
        2)const成员函数。如果你定义了一个const对象,那么对于这个对象的一切非const成员函数的调用,编译器为了保证对象的const特性,都会禁止并在编译期间报错。所以如果想让成员函数能够在const对象上进行操作的话,就要把这个函数声明为const成员函数。假如f()是类中的成员函数,它的声明形如:
                    int f() const;
              const放在函数名的后面,编译器会对这个函数进行检查,在这个函数中的任何试图改变成员变量和调用非const成员函数的操作都被视为非法。
注:类的构造函数和析构函数都不能是const函数。
        3)建立了一个const成员函数,但仍然想用这个函数改变对象内部的数据。有两种方法可以实现,如果要求不改变原来类的任何东西,只从当前这个const函数入手,那么只有使用类型强制转换方法(const_cast())。
        实例如下:假如有一个叫做X的类,它有一个int成员变量r,需要通过一个C++ const成员函数f()来对这个r变量进行操作,代码如下:
                    void X::f()  const
                    { (const_cast(this))->++r;}    // 通过this指针进行类型强制转换实现
        另一种方法就是使用关键字:mutable。如果 成员变量在定义时是这个样子的:
                    mutable int r;
        那么它就告诉编译器这个成员变量可以通过const成员函数进行改变。
                          
(6) 若要使const变量像其他对象一样工作,即只在一个文件中定义const,而在其他多个文件中声明并使用它,那么就要对于const变量不管是定义还是声明都添加“extern”关键字:
  // file_1.c定义并初始化一个const
  extern const int bufsize = 512;
  // file_2.h
  extern const int bufsize;      // 与file_1.c中定义的bufsize是同一个。
  “file_2.h”文件中的声明也由“extern”做了限定,其作用是指明bufsize并非本文件所独有,它的定义将在别处出现。
 
(7) 可以把引用绑定到const对象上,称为对常量的引用。
    const int c1 = 1024;
    const int &r1 = c1;    // 正确,引用及其对应的对象都是常量
    int &r2 = c1;          // 错误,试图让一个非常量引用指向一个常量对象
    假设上句是合法的,则可以通过r2来改变它引用对象的值,这显然是不正确的。        
         
(8) “对const的引用”,简称为“常量引用”,要时刻记得这就是个简称而已。引用的类型必须与其所引用的对象的类型一致,但是也有例外。
  1)第一种例外情况就是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可; 尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式:
    int i = 42;
    const int &r1 = i;                    // 允许将const int& 绑定到一个非常量int对象上
    const int &r2 = 42;                 // 正确:r2是一个常量引用
    const int &r3 = r1 * 2;            // 正确:r3是一个常量引用
    int &r4 = r1 * 2;                      // 错误:r4是一个普通的非常量引用
注:常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是常量未作限定。
 
(9) 指向常量的指针:不能用于改变其所指对象的值。
  1)要想存放常量对象的地址,只能使用指向常量的指针:
    const double pi = 3.14;
    double *ptr = &pi;        // 错误:ptr是一个普通指针
    const double *cptr = &pi;  // 正确:cptr可以指向一个双精度常量
    *cptr = 42;               // 错误:不能给*cptr赋值
  2)指针的类型必须与其所指向对象的类型一致,但是也有例外。
    a)第一种例外情况是允许令一个指向常量的指针指向一个非常量对象:
      double dval = 3.14;
      cptr = &dval;          // 正确:但是不能通过cptr改变dval的值
注:指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他路径改变。
注:试试这样想:所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,它们觉得自己指向常量,所以自觉地不去改变所指对象的值。

(10) const指针:常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的地址)就不能再被改变。
  1)将“*”放在“const”关键字之前用以说明指针是一个常量,这样的书写形式隐含着一层意思,即不变的是指针本身而并非指向的那个值:
            int errNumb = 0;
            int *const curErr = &errNumb;           // curErr将一直指向errNumb
            const double pi = 3.14;
            const double *const pip = &pi;        // pip是一个指向常量对象的常量指针    
      指针本身是一个常量并不意味着不能通过指针修改其所指对象,能否这样做完全依赖于所指对象的类型。
  2)指针本身是不是常量以及指针所指的对象是不是常量是两个相互独立的问题。因此,用名词“顶层const”表示指针本身是常量;而用名词“底层const”表示指针所指的对象是一个常量。更一般的,“顶层const”表示任意的对象是常量;“底层const”则与指针和引用等复合类型的基本类型有关。
    a)执行拷贝操作时,不考虑“顶层const”,但拷入和拷出的对象必须具有相同的“底层const”,或者两个对象的数据类型必须能够转换。一般,非常量可以被转换为常量,反之则不行。
   
(11) 常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。
  1)显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。
  2)一个对象(表达式)是不是常量表达式由它的数据类型和初始值共同决定:
        const int max_files = 20;               // max_files是一个常量表达式
        const int limit = max_files + 1;              // limit是一个常量表达式
        int staff_size = 27;                    // staff_size不是常量表达式
        const int sz = get_size();              // sz不是常量表达式
    尽管staff_size的初始值是个字面值常量,但由于它的数据类型只是一个普通int而非const int,所以它不属于常量表达式。另一方面,尽管sz本身是一个常量,但它的具体值直到运行时才能获取到,所以也不是常量表达式。

(12) constexpr变量:c++11标准规定,允许将变量声明为“constexpr”类型以便由编译器来验证变量的值是否为一个常量表达式。
  1)声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:
          constexpr int mf = 20;              // 20是常量表达式
          constexpr int limit = mf + 1;       // mf + 1是常量表达式
          constexpr int sz = size();          // 只有当size是一个constexpr函数时,才是一条正确的声明语句
注:一般来说,如果你认定变量是一个常量表达式,那就把它声明成“constexpr”类型。

(13) 算术类型、引用和指针都属于字面值类型;自定义类型、IO库、string类型则不属于字面值类型,也就是不能被定义为constexpr。
一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。
注:函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。相反的,定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针。

(14) 必须明确,在constexpr声明中如果定义了一个指针,那么限定符constexpr仅对指针有效,与指针所指对象无关:
  const int *p = nullptr;           // p是一个指向整型常量的指针
  constexpr int *q = nullptr;       // q是一个指向整型的常量指针
  constexpr将它定义的对象置为顶层const。
posted on   菜鸟1234567  阅读(0)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示