zoukankan      html  css  js  c++  java
  • C++ const总结

    编程中是否遇到这样情况?

    (基本内容来源于C++ Prime)

    1、用一个变量表示缓冲区的大小。当我们觉得不合适时,直接改变变量值即可,可以很方便调整缓冲区大小

    2、但要警惕程序不小心改变了这个值

    OK,遇到这样情况,使用const修饰符即可。

    言简意赅,总结一下。

    首先上目录:

    1、const对象必须初始化。

    2、默认情况下,cosnt对象只在文件内有效

    3、const的引用

    4、const与指针

    5、顶层const 和 底层const

    6、C++常量折叠

    7、const函数

    8、const成员变量

    1、const对象必须初始化。

    因为const对象一旦创建,就不能改变了。

    2、默认情况下,cosnt对象只在文件内有效

    下面论述一下const对象和普通对象的区别。

    (转载于http://blog.csdn.net/yipiantiandi/article/details/5822564)

    (const对象默认作用域为:文件作用域)

    (普通变量默认作用域:整个程序)

    对于一般的对象 如 int a=9;;我们知道它的作用域是整个程序的,在1.cpp和2.cpp中包含同一个定义了int a=9;的头文件,因为int a=9;作用域是整个程序,所以会产生错误。

    那为什么const int a=9;不会产生错误呢。原因就是const int a=9;的默认作用范围是文件作用域的。即,尽管在1.cpp和2.cpp中包含同一个定义了const int a=9;的头文件,但由于const int a=9;是文件作用域的,包含了头文件的1.cpp和2.cpp尽管都包含了const int a=9;但他们是各自文件内的const对象,两者互不相干,就像不同函数中定义了相同的变量名一样。

     所以,通过在头文件中定义const对象,可以实现多个文件使用相同的常量值,这在实际的程序中很常见。

    那么如何使用其他文件中的const

    说到这里,有一点疑问需要解决:如果想在别的文件中访问本文件中的const对象该怎么办,如果都是单是在别的文件中使用extern const int a = 9;的话,不会起任何作用,因为已经提示const int a = 9;是文件作用域的。

    所以应该在需要被其他文件使用的const对象定义成这样:extern const int a = 9;而在别的需要使用这个const对象的地方声明:extern const int a;前者是定义,后者是声明。这样就可以使用了。

    3、const的引用

    引用前带const修饰符 被引用前带const修饰符 附注
    对const变量的const引用
    引用认为它引用的变量不可以改变,但实际可以(代码附注1)
    不可以(代码附注2)
    就是普通的引用

    代码附注1:

        int i=42;
        const int &r_i = i;
        cout<<"r_i: "<<r_i<<endl;
        cout<<"i:   "<<i<<endl;
        
        i=41;//可以
        cout<<"r_i: "<<r_i<<endl;
        cout<<"i:   "<<i<<endl;
        
        r_i = 40;//报错
        cout<<"r_i: "<<r_i<<endl;
        cout<<"i:   "<<i<<endl;

    detail it:对const的引用可能是引用一个并非const的对象

     常量引用仅对引用可以参与的操作做出了限定,对于引用的对象本身是不是常量未做限定。因为对象也可能是一个非常量,所以允许通过其他途径改变它的值。

    代码附注2

        const int r1=42;
        int &r2 = r1;//试图让一个非常量引用指向一个常量对象。如果该初始化合法,则可以通过r2来改变它引用对象的值,这显然不正确。

    4、const与指针

    指针 被指数据 附注
    变量 变量 普通的指针
    变量 常量 不可以
    常量 变量 可以。常量指针“自以为是”,认为自己指向了常量,所以自觉的不去改变所指对象的值。实际上,这个值是可以改变的
    常量 常量 可以。
    Pointer to Constant (常量指针/常指针) Pointer Constant(指针常量)

    const int*p1; 

    const int x = 1;

    p1 = &x; 

    int*const p2 = &x;  

    p1是一个指针

    它是一个常量

    p2是一个指针

    它指向一个常量

    准则:在前先读,在前不变 const int *x  int* const y

    * (指针)和const(常量)谁在前先读谁;

    * 代表被指的数据,名字代表指针地址

    const在谁前面谁就不允许改变。

    const在*前面,该数据不能改变 const在地址前面,该地址不能变化
         

    (此小节内容来自网易公开课,北京邮电大学,崔毅东老师的《C++程序设计入门(上)》)

    5、由上一节的内容,可以引申出一个概念:

    顶层const 和 底层const

    该对象是const,说明其为顶层const

    该对象指向或者引用的变量是const,说明其为底层const

    const int ci = 42; // 不能改变 ci 的值,这是一个顶层 const

    const int *p2 = &ci; // 允许改变 p2 的值,这是一个底层 const

    const int *const p3 = p2; // 靠右的 const 是顶层 const ,靠左的是底层 const

     

     6、C++常量折叠

    常量与宏定义一直被面试官津津乐道

    转载于http://blog.csdn.net/yby4769250/article/details/7359278

    看个code先,

    #define PI 3.14
    int main()
    {
        const int r = 10;
    
        int p = pI; //这里会在预编译阶段产生宏替换,PI直接替换为3.14,其实就是int p = 3.14;
        int len = 2*r; //这里会发生常量折叠,也就是对常量r的引用会替换成他对应的值,相当于int len = 2*10;
        return 0;
    }

    如上述代码中所述,常量折叠表面上的效果和宏替换是一样的,只是,“效果上是一样的”,

    而两者真正的区别在于,

    1、宏是字符常量,在预编译完宏替换完成后,该宏名字会消失,所有对宏如PI的引用已经全部被替换为它所对应的值,编译器当然没有必要再维护这个符号。

    2、而常量折叠发生的情况是,对常量的引用全部替换为该常量如r的值,但是,常量名r并不会消失,编译器会把他放入到符号表中,同时,会为该变量分配空间,栈空间或者全局空间

    为了能更清楚的体现出常量折叠,下面做几个对照实验,看代码和输出便了然:

    int main()
    {
        int i0 = 11;
    
        const int i=0;         //定义常量i
        int *j = (int *) &i;   //看到这里能对i进行取值,判断i必然后自己的内存空间
                    //请注意,
    int *j = &i;是不对的哦,我们在第4节讲过~
        *j=1;                  //对j指向的内存进行修改
        
    printf("%d %d %d %d ",&i,j,i,*j); //观看实验效果 const int ck = 9; //这个对照实验是为了观察,对常量ck的引用时,会产生的效果 int ik = ck; int i1 = 5; //这个对照实验是为了区别,对常量和变量的引用有什么区别 int i2 = i1; return 0; }

    上面的代码会输出:

    0012ff7c
    0012ff7c

    0

    1

    这能说明什么,至少能说明两点:

    1、i和j地址相同,指向同一块空间,i虽然是可折叠常量,但是,i确实有自己的空间

    2、i和j指向同一块内存,但是*j = 1对内存进行修改后,按道理来说,*j==1,i也应该等于1,而实验结果确实i实实在在的等于0,这是为什么呢,就是本文所说的内容,i是可折叠常量,在编译阶段对i的引用已经别替换为i的值了,也就是说

    printf("%d
    %d
    %d
    %d
    ",&i,j,i,*j)

    中的i已经被替换,其实已经被改为

    printf("%d
    %d
    %d
    %d
    ",&i,j,0,*j)

    为了使实验更具说服力,直接上汇编代码,比较实验的不同点:

    4:    int main()
    5:    {
    00401030   push        ebp
    00401031   mov         ebp,esp
    00401033   sub         esp,5Ch
    00401036   push        ebx
    00401037   push        esi
    00401038   push        edi
    00401039   lea         edi,[ebp-5Ch]
    0040103C   mov         ecx,17h
    00401041   mov         eax,0CCCCCCCCh
    00401046   rep stos    dword ptr [edi]
    6:        int i0 = 11;
    00401048   mov         dword ptr [ebp-4],0Bh
    7:
    8:        const int i=0;
    0040104F   mov         dword ptr [ebp-8],0 //睁大眼睛,编译器确实为常量i分配了栈空间,并赋值为0
    9:        int *j = (int *) &i;
    00401056   lea         eax,[ebp-8]
    00401059   mov         dword ptr [ebp-0Ch],eax
    10:       *j=1;
    0040105C   mov         ecx,dword ptr [ebp-0Ch]
    0040105F   mov         dword ptr [ecx],1
    11:                                            //再看看下面的对比实验,看出对常量的引用和变量的引用的区别
    12:       const int ck = 9;
    00401065   mov         dword ptr [ebp-10h],9   //为常量分配栈空间 
    13:       int ik = ck;
    0040106C   mov         dword ptr [ebp-14h],9   //看到否,对常量ck的引用,会直接替换为常量的值9,再看下面的实验
    14:
    15:       int i1 = 5;
    00401073   mov         dword ptr [ebp-18h],5
    16:       int i2 = i1;                         //这里引用变量i1,对i2进行赋值,然后看到否,对常量i1引用没有替换成i1的值,而是去栈中先取出i1的值,到edx寄存器中,然后再把值mov到i2所在的内存中
    0040107A   mov         edx,dword ptr [ebp-18h]
    0040107D   mov         dword ptr [ebp-1Ch],edx
    17:
    18:
    19:       return 0;
    00401080   xor         eax,eax
    20:
    21:   }

    总结:常量折叠说的是,在编译阶段,对该变量进行值替换,同时,该常量拥有自己的内存空间,并非像宏定义一样不分配空间,需澄清这点

    7、const修饰函数

    const 放在函数后表示这个函数是常成员函数, 常成员函数是不能改变成员变量值的函数。
    const 限定符,它把一个对象转换成一个常量。

    1、非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员:

    2、非常量成员函数不能被常量实例调用,因为它可能企图修改常量的数据成员

    举例:
    为了使成员函数的意义更加清楚,我们可在不改变对象的成员函数的函数原型中加上const说明:
    class Point
    {
    public:
    int GetX() const;
    int GetY() const;
    void SetPt (int, int);
    void OffsetPt (int, int);
    private:
    int xVal, yVal;
    };
    const成员函数应该在函数原型说明和函数定义中都增加const限定:
    int Point::GetY() const
    {
    return yVal;
    }
    class Set {
    public:
    Set (void){ card = 0; }
    bool Member(const int) const;
    void AddElem(const int);

    };
    bool Set::Member (const int elem) const
    {
    }
    非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员:
    const Set s;
    s.AddElem(10); // 非法: AddElem不是常量成员函数
    s.Member(10); // 正确
     
     
    8、const成员变量
    #include <iostream>
    using namespace std;
    
    class A
    {
        public:
            A(int size) : SIZE(size) {};
        private:
            const int SIZE;
    };
    
    int main()
    {
        A a(100);
    }
    • 在类中声明变量为const类型,但是不可以初始化
    • const常量的初始化必须在构造函数初始化列表中初始化,而不可以在构造函数函数体内初始化

    此时的const变量属于具体的一个对象,如何在整个类中都恒定不变呢?

     答案是利用枚举,举例

    #include <iostream>
    using namespace std;
    
    class A
    {
        private:
            enum {SIZE = 100};
        public:
            int array[SIZE];
    };
    
    int main()
    {
        A a;
    }

    枚举常量不会占据对象的存储空间,在编译时被全部求值

    但是,它隐含的数据对象类型为整形,不能表示其他类型。

     
  • 相关阅读:
    LeetCode Single Number
    Leetcode Populating Next Right Pointers in Each Node
    LeetCode Permutations
    Leetcode Sum Root to Leaf Numbers
    LeetCode Candy
    LeetCode Sort List
    LeetCode Remove Duplicates from Sorted List II
    LeetCode Remove Duplicates from Sorted List
    spring MVC HandlerInterceptorAdapter
    yum
  • 原文地址:https://www.cnblogs.com/wuqi/p/4573028.html
Copyright © 2011-2022 走看看