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;
    }

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

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

     
  • 相关阅读:
    Civil 3D .NET二次开发第11章代码升级至2018版注意事项
    创建道路曲面
    ObjectARX® for Beginners: An Introduction
    mshcMigrate制作的mshc文件中有链接打不开
    Word 2013无法发布文章到博客园
    ionic 安装插件报错:源文本中存在无法识别的标记
    typescript文件中 使用回调函数无法调用函数外的变量和方法的办法
    ionic2---自定义插件
    angular2----使用swiper做轮播图
    angular2----router
  • 原文地址:https://www.cnblogs.com/wuqi/p/4573028.html
Copyright © 2011-2022 走看看