zoukankan      html  css  js  c++  java
  • 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)》

  • 相关阅读:
    LeetCode "Jump Game"
    LeetCode "Pow(x,n)"
    LeetCode "Reverse Linked List II"
    LeetCode "Unique Binary Search Trees II"
    LeetCode "Combination Sum II"
    LeetCode "Divide Two Integers"
    LeetCode "First Missing Positive"
    LeetCode "Clone Graph"
    LeetCode "Decode Ways"
    LeetCode "Combinations"
  • 原文地址:https://www.cnblogs.com/suzhou/p/3638971.html
Copyright © 2011-2022 走看看