• const关键字浅析


    1  const变量

    const double PI = 3.14159;


    定义之后不能被修改,所以定义时必须初始化。

    const int i, j = 0;  // error: i is uninitialized const

    const变量默认为文件的局部变量,此变量只存在于那个文件中,不能被其他文件访问。通过指定const变更为extern,就可以在整个程序中访问const变量。

    // file_1.c
    extern const int bufSize = fcn();
    
    // file_2.c
    extern const int bufSize;
    for (int index = 0; index != bufSize; index++)
        //... 


    2  const和指针

    const和指针有两种交互类型:指向const对象的指针和const指针。

    2.1 指向const对象的指针 —— “自以为指向const对象的指针”

    const double *cptr; // cptr may point to a double that is const
    在上面的语句中,const限定的是cptr指针所指向的对象,而不是cptr本身,也就是说,cptr本身并不是const, 所以不需要对cptr进行初始化。如果需要的话,允许给cptr重新赋值,但是不能通过cptr修改其所指对象的值:
    *cptr = 42; //error: *cptr might be const

    C++为了保证不允许用指针来改变const对象的值,强制要求指向const对象的指针必须具有const特性,所以把const对象的地址赋给一个普通的非const对象的指针也会导致编译时错误:
    const double pi = 3.14;
    double * ptr = π   // error: ptr is a plain pointer
    const double * cptr = π  // ok: cptr is a pointer to const

    这条规则对于void *指针也是必须的, 即不能使用void *指针保存const对象的地址,而必须使用const void *型的指针保存const对象的地址。
    const double pi = 3.14;
    const void *cpv = π  // ok
    void *pv = π  //error

    允许把非const对象的地址赋给指向const对象的指针,例如:
    double pi = 3.14;
    const double * cptr = π // ok, but can't change pi through cptr

    2.2 const指针

    const指针,指针本身的值不能修改。和其他const常量一样,const指针的值是不能修改的,这就意味着不能使const指针指向其他对象。
    double pi = 3.14;
    double pi2 = 6.28;
    double * const conptr = π
    conptr = &pi2;  // error: conptr is const

    2.3 指向const对象的const指针

    可以如下定义指向const对象的const指针:
    const double pi = 3.14;
    // pi_ptr is const and points to a const object
    const double * const pi_ptr = π
    可以从右向左阅读上述声明语句:”pi_ptr首先是一个const指针,指向double类型的const对象“。


    3  const引用——“自以为指向const对象的引用”

    在说明const引用之前,先复习一下引用的一些特性:

    • 引用必须和同类型的对象相关联;
    • 初始化是指明引用指向哪个对象的唯一方法;
    • 当引用初始化后,它就保持绑定到初始化时指向的对象,不能将引用绑定到另一个对象。

    int a = 1024;
    int &refa1 = a;  // ok
    int &refa2; // error
    int &refa3 = 2048; // error: mismatch type

    现在来看看特殊的引用:const引用。

    const引用是指向const对象的引用。之所以不是“引用是const”,是因为引用本身就被默认为const,当一个引用被定义时,必须被初始化,绑定到一个对象上,并且不可以改变。

    const int ival = 1024;
    const int &refVal = ival;   //ok
    int &ref2 = ival; //error: nonconst reference to a const object.

    所谓的“自以为指向const对象的引用”的意思是,const引用也可以指向非const对象,效果就是不可以通过这个引用来修改所指对象的值,即,这个引用是只读的。

    int z = 0;
    const int &refZ = z;
    refZ = 1;  //error.

    const引用和普通引用的一个不同的地方是:const引用可以初始化为不同类型的对象或者右值(如字面值常量)。

    int i = 42;
    const int &refI = 42;  // ok
    const int &refI2 = i + 42; //ok

    对于引用绑定到不同类型的对象:

    double dval = 3.14;
    const int &ri = dval;
    编译器会把这些代码转换成如下形式的代码:

    double dval = 3.14;
    int temp = dval;  // create temporary int from the double
    const int &ri = temp; //bind ri to that temporary

    4  函数中使用const

    4.1 指向const对象的指针的形参

    比较常见的用法是保证传入的参数不被修改,所以一般将函数参数声明为指向const对象的指针:
    void function(const char* Var); // 参数指针所指内容为常量,不可改变

    4.2 const引用类型的形参

    void function(const string & s1); // s1是一用,所以不复制形参,又因为形参是const引用,所以该函数不能修改是s1引用对象的内容。
    优点:避免复制形参,提高效率,同时防止修改实参。

    同时更大的优点是,const引用类型的形参更灵活:

    如上述定义的函数传递的形参可以是一个一个字符串常量,如:

    function("hello world");
    因为const引用形参可以与不同类型的对象相关联。

    但是如果将函数声明为如下的形式:

    void function(string & 2);

    由于非const引用形参只能与完全同类型的非const对象关联,所以再传递字符串常量就会发生编译错误。


    5  类中使用const

    5.1 const成员函数

    class A 
    void function(const char* Var);
    {
            void function_1();
            void function() const;
    }

    用这种方式使用const的函数称为常量成员函数。

    在解释这个const所起的作用之前,回忆一下,每个成员函数都有的一个额外的隐含的形参this。

    考虑下面的调用语句:

    a.function_1();

    编译器会这样重写这个函数调用:

    A::function_1(&a);
    即隐含的this初始化为调用函数的对象的地址。

    那么跟在函数声明的形参表后面的const所起的作用就是:改变this形参的类型。

    在调用a.function()时,隐含的this形参将是一个指向a对象的const A*类型的指针。


    const成员函数的特性:

    • const成员函数不可以修改它所在对象的任何一个数据成员
    • const成员函数能够访问对象的const成员,而其他成员函数不可以
    • const对象,指向const对象的指针或引用只能用于调用其const成员函数,如果尝试用它们来调用非const成员函数,是错误的。

    5.2 const成员变量

    初始化const成员变量必须在执行构造函数的函数体之前完成,所以,唯一的机会就是在构造函数初始化列表中初始化const成员变量。

    Class A 
    {
        ...
    private:
        const int nValue;
    
    public:
        ......
        A(int x): nValue(x) { ... };
    
    }

    下面的构造函数是错误的:

    class ConstRef 
    {
    public:
        ConstRef(int ii);
    private:
        int i;
        const int ci;
        int &ri;
    };
    
    ConstRef::ConstRef(int ii)
    {
        i = ii;
        ci = ii;  // error: cannot assign to a const
        ri = i;
    }

    正确的构造函数应该这样:

    ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) {}
    没错,引用类型数据成员和const成员变量一样,初始化的唯一机会是在构造函数初始化列表中。

    5.3 特殊的整型const static 成员

    先回忆一下类的static成员的基本性质:

    通常,static数据成员不像普通数据成员,static成员不是用过类构造函数进行初始化,由于static成员只能被初始化一次,所以应该在定义时初始化,且在类的定义体外部定义。

    #include<iostream>
    using namespace std;
    
    class Tester
    {
    public:
    	static int a;
    };
    
    int Tester::a = 10;
    
    int main()
    {
    	cout << Tester::a << endl;
    	return 0;
    }
    不需要实例化这个类,即可访问static成员。

    这个规则的一个例外就是:只要初始化式是一个常量表达式,整型const static 数据成员就可以在类的定义体中进行初始化,需要注意的是,该数据成员仍必须在类的定义体之外进行定义,而不必再指定初始值。

    #include<iostream>
    using namespace std;
    
    class Tester
    {
    public:
    	const static int b = 0;
    };
    
    const int Tester::b;
    
    int main()
    {
    	cout << Tester::b << endl;
    	return 0;
    }

    这个程序在电脑上编译通过,但是遭到管家报警自动删除,警报为后门程序。。




    参考资料:

    《C++Primer(4th)》

    《C++高级编程(2th)》

  • 相关阅读:
    多层次子查询的sql执行顺序的问题
    RestTemplate不落地中转文件
    Gitbook从安装到使用【转】
    Linux离线安装依赖包技巧
    CentOS7 配置环境变量断开ssh后失效
    分页查询排序问题
    地图坐标的转换
    FeatureLayer图层的属性查询方式(query的使用方法)
    使用LayerView.effect进行点的高亮显示
    DQL + 排序
  • 原文地址:https://www.cnblogs.com/suzhou/p/3638971.html
Copyright © 2020-2023  润新知