• 二、常量


    目录

    1、#define定义常量,好与坏

    2、const关键字(各种const对象,指针,引用,函数,对应的引用等等)

    常量就是在运行期间,值一直不变。c语言用#define定义,宏常量。C++里面用#define和const定义常量。

    转:http://blog.csdn.net/love_gaohz/article/details/7567856

    http://blog.sina.com.cn/s/blog_60be7ec80100gzhm.html

    1、define定义常量

    定义的是全局常量

    define宏是在预处理阶段展开

    没有类型,仅仅是展开,不做类型展开

    仅仅是展开,多少个地方使用,就有多少个地方展开。(宏定义不分配内存,变量定义需要分配内存)

    只是替换字符串,容易产生意想不到的错误(边际效益)

    2、const常量

    2.1、 const和define比较

    • 在编译阶段运行使用
    • 有具体的类型,在编译阶段要进行类型检查
    • const常量会在内存中分配内存,但是只是进行一次分配
    • 集成化的调试工具可以进行调试(这个不知道)
    • 可以定义局部常量,也可以定义全局常量(我觉得可以用static,这个有待后面的搜集学习)

    转别人的:

    (1) 编译器处理方式不同
      define宏是在预处理阶段展开。
      const常量是编译运行阶段使用。
    (2) 类型和安全检查不同
      define宏没有类型,不做任何类型检查,仅仅是展开。
      const常量有具体的类型,在编译阶段会执行类型检查。
    (3) 存储方式不同
      define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)
      const常量会在内存中分配(可以是堆中也可以是栈中)。
    (4)const  可以节省空间,避免不必要的内存分配。 例如:  
            #define PI 3.14159 //常量宏  
            const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ......  
            double i=Pi; //此时为Pi分配内存,以后不再分配!  
            double I=PI; //编译期间进行宏替换,分配内存  
            double j=Pi; //没有内存分配  
            double J=PI; //再进行宏替换,又一次分配内存!  
            const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝(因为是全局的只读变量,存在静态区),而 #define定义的常量在内存中有若干个拷贝。 
    (5) 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
    (6) 宏替换只作替换,不做计算,不做表达式求解;
           宏预编译时就替换了,程序运行时,并不分配内存。
    const 与 #define的比较
        C++ 语言可以用const来定义常量,也可以用 #define来定义常量。但是前者比后者有更多的优点:
    (1)   const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
    (2)   有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
    View Code

    2.2、 C++中尽量使用const,避免使用define

    对外被使用的常量应放在头文件,内部使用的常量放在源文件的头部。

    可以将常量都定义在一个文件里面便于管理

    2.3、类中的const成员

    const定义的常量在函数执行和对象销毁之后其空间会被释放。

    对于类中的仅仅用const修饰的常量,相对于对象来说是不变的,但是对于类来说是变的。因为一个类可以创建很多对象,这样在进行构造函数初始化的时候,可以进行初始化。

    所以在类声明const成员的时候,不能就直接给const常量进行赋值。

    class Test{
        public:
            const int size = 10;//error,不能在类声明中给const常量赋值
            int array[size];//size未知
    }

    在类中,如何进行const赋值,只能通过构造函数的初始化列表进行赋值,而且还必须是在初始化列表里面进行初始化。这样的话就可以对于每一个对象有不同的const常量。

    class Test{
        public:
            const int size;
            Test(int s):size(s){
                ...
                ...
            }
    };

    那如何才能做到对于整个类来说不变的常量,可以使用:枚举,static const

    枚举:

    class Test{
        public:
            Test():constValue(9){}
            enum {size1 = 100, size2 = 200};//枚举
        private:
            const int constValue;//这个是const常量,只能在构造函数的初始化列表里面进行初始化。
            static int staticValue;//这个是静态变量。整个类可以使用,只有一次初始化,可以改变值
            const static int allValue;// static const is ok. 这个是静态常量
    };
    int Test::staticValue = 10;//不能在构造函数初始化列表初始化,不能在声明处初始化,因为不属于某个对象
    const int Test::allValue = 10;//给静态变量赋值的时候,不用加static,上面的staticValue也没有加

    总结:const成员的初始化:

    • 在类中,只有const修饰的常量,只能在构造函数的初始化列表进行初始化,类对应每个对象的const的常量值可以不一样
    • 在类中,有const 和static组合修饰,不能在构造函数初始化列表初始化,因为不属于对象。要在类外进行初始化,同时要加上const,不用加static。
    • 在函数内,const修饰的变量必须在声明的时候就进行初始化,且该变量不再重新变。
    • const对象默认为文件的局部变量

    2.4、const默认为文件的局部变量

    const默认为文件的局部变量。

    在全局作用域里定义非const变量,它在整个程序里面都可以进行访问。

    //file_1.cc
    int counter; //定义
    //file_2.cc
    extern int counter; //可以通过extern用counter
    ++counter;

    但是对于在全局作用域声明的const变量是定义为该文件的局部变量。只存在于那个文件,不能被其他文件访问,如果希望其他文件能够访问,必须在前面加上extern,这样,在整个程序中,都可以访问这个const变量。

    //file_1.cc
    //const必须在声明的时候进行初始化(函数和全局变量)
    extern const int bufSize = fcn();
    //file_2.cc
    extern const int bufSize;//可以在这个文件使用
    if (int index = 0; index != bufSize; ++ index )
    ...
    ...
    • 这是因为非const变量默认为extern,但是const变量没有这个默认
    • 如果要const其他文件能够访问,要显示的指定为extern。当然,在其他地方使用的文件处,也需要加上extern,这个对于非const和const都是必须的。

    2.5、 const引用

    引用(reference)是对象的另外一个名字,在对引用进行各种操作,也是对原始对象的各种操作。引用主要用作函数的行参。

    引用是一种复合类型:用其他类型定义的类型。就是不能直接用常量定义。一定要关联到其他的类型。

    不能定义引用类型的引用,但是可以定义任何其他类型的引用。

    引用必须用其他类型初始化。必须初始化。

    引用一旦绑定到一个对象,就不能再绑定到其他对象。

    int iValue = 10;

    int &rValue = iValue;//这个就是引用

    const引用是指const对象的引用。

    • 既然是指向const对象的引用,const对象不能改变,其引用也不能够被改变。

       所以const对象的引用必须满足:1、若对象为const,则引用为const

       const int ival = 1024;

       const int &rval = ival;//这个引用就是指向const对象的引用,不能改变,因为rval与ival相关联,ival不能改变,其rval也不能改变,所以两个都必须为const。

    • 对象可以不为const,引用可以为const

        int i = 42;

        const int &r = 42;

        const int &r2 = r + i;

        或者

        double dval = 3.14;

        const int &ri = dval;

        因为编译器会把代码转换为以下形式:

        int temp = dval;

        const int &ri = temp;

        引用绑定到了ri上面。dval的改变不会影响到ri的值。

    总结

    非const引用只能绑定到与该引用同类型的对象。

    const引用的赋值可以绑定到:1、相同类型的const,非const对象。2、相关类型的const、非const对象。3、绑定到右值

    利用const引用避免了赋值。

    书上的话:如果使用引用行参的唯一目的是避免复制,则应定义为const

    2.6 const与指针

    这个比较复杂,而且很难记;const和指针的关系有两个:指向const对象的指针,const指针

    2.6.1 指向const对象的指针

     如果指针是指向const对象,则不允许用指针改变其所指的const值。

    const double *cptr;//该为指向const对象的指针

    const限定的是cptr指针所指的对象,并非cptr指针,cptr并不具有const特性,所以在定义的时候并不需要进行初始化。可以对cptr进行重新赋值,即可以指向其他对象。但是不能同构cptr修改所指向的内容。

    *cptr = 42;//错误的,不能改变所指向对象的内容

    ++cptr;//这个是对的,因为该指针没有const特性。

    • 注意:::const的对象的地址只能赋值给指向const对象的指针。如果赋值给指向非const对象的指针会出错,因为这个指针可以改变所指向的内容。
    const double pi = 3.14;
    double *ptr = π//错误,用指向非const对象的指针指向const对象
    const double *cptr = π//这个是正确的为指向const对象的指针指向了const对象

    不能用void*保存const对象的指针,必须用const void *保存const对象的指针。

    • 可以把非const对象的地址赋值给指向const对象的指针。
    double dval = 3.14;
    const double *cptr = &dval;

    尽管dval不是const的,也不能通过cptr来改变dval的值,可以通过其他方式改变dval的值。

    总结

    • 指向const对象的指针不管怎么样都不能改变自己所指向的对象。
    • 指向const对象的指针可以指向const对象,也可以指向非const对象。
    • 指向const对象的指针常用作函数的行参。确保传递给函数的实际对象在函数中不因为行参而被破坏
    • 所指向的基础对象不能改变,但是该指针可以改变,这个指针可以指向多个对象。

    2.6.2 const指针

    const指针:指针不能更改,但是指针所指向的对象的值可以更改(如果对象不是const,可以更改,如果是const,则不能更改)。即这个指针只能指向一个对象,可以通过这个指针改变对象的值。

    int errNumb = 0;

    int *const curErr = &errNumb;

    ++curErr;//出错,因为指针不能更改

    *curErr = 1;//对的

    2.6.3 这两个不好记:

    先忽略类型名,看const离哪个近,就修饰谁;

    const *p;//const修饰*p,p是指针,*p是指针指向的对象,不可变

    *const p;//const修饰p,p不可变,p指向的对象可变。

    const *const p;//前一个修饰*p,后一个修饰p

    总结

    • 指向const对象的指针,指针可变,对象内容不可变,不管指向的对象是不是const,都不能通过指针来修改这个对象的内容
    • const指针,指针不可变,对象则根据对象的类型判断可变。

    有一个需要注意:

    string s;

    typedef string *pstring;

    const pstring cstr1 = &s;

    pstring const cstr2 = &s;

    string *const cstr3 = &s;

    这三个都是一样的,都是:指向string类型对象的const指针

    2.7 const参数,函数

    const参数:对于参数在函数中不需要被改变,将参数定义为const是较好的选择,因为这样可以传递常量进来。

    int strlen(const char* str);//这个就可以传递非const字符串,const字符串,以及"fjdajf"这种字符常量。如果没有const的话,则不能传递最后一种情况的字符串。前两种刚好验证了指向const对象的特性。

    const函数:将函数定义为const,相当于修饰返回值,不可改变

    int fun(void) const;

    const差不多就这些吧。如果还有,以后再加。

    写到这,突然觉得行参可以弄个专题:因为涉及到引用传递数组这个问题。还有volatile这个关键字。

  • 相关阅读:
    问题堆栈区39+40
    ListView优化分页优化
    AsyncTask理解- Day36or37
    Activity是如何挂载Pargment的Day35
    BroadcastReceiver和Intetnt的理解 Day34
    深入理解自定义ListView
    手势识别=读取手机联系人=ContentResolver-Day3
    Android本地JUnit Text
    Java——(一)一切都是对象
    SciTE: 中文字符支持问题
  • 原文地址:https://www.cnblogs.com/leewiki/p/3901820.html
Copyright © 2020-2023  润新知