• 第8章 常量


      值替代

      当用C语言进行程序设计时,预处理器可以不受限制地建立宏并用它来替代值。因为预处理器只做些文本替代,它既没有类型检查概念,也没有类型检查功能,所以预处理器的值替代会产生一些微小的问题,这些问题在C++中可以通过使用const值而避免。

      比如:

        #define BUFFERSIZE 100

      BUFFERSIZE是一个名字,它只是在预处理期间存在,因此它不占用存储空间且能放在一个头文件里,目的是为使用它的所有编译单元提供一个值。使用值替代而不是使用所谓的“不可思议的数”,这对于支持代码维护是非常重要的。

     

      头文件里的const

      要使用const而非#define,同样必须把const定义放进头文件里。这样通过包含头文件,可把const定义单独放在一个地方并把它分配给一个编译单元。C++中的const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。

      可以参考:http://www.cnblogs.com/sheshiji/p/3427896.html

      const可以用于数组,但必须保证编译器不会复杂到把一个数组保存到它的符号表中,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储空间”。然而,不能在编译期间使用它的值,因为编译器在编译期间不需要知道存储的内容。这样,就能明白下面的代码是非法的:

        const int i[] = { 1,2,3,4 };
        float f[i[3]];  // illegal

      在一个数组定义里,编译器必须能产生这样的代码,它们移动栈指针来存储数组。在上面的非法定义里,它不能在数组定义里找到一个常数表达式。

     

      C语言的区别

      Cconst的意思是“一个不能被改变的普通变量”,const常量总是占用存储而且它的名字是全局的。这样,C编译器不能把const看成一个编译期间的常量。在C中,如果写:

        const int bufsize = 100;
        char buf[bufsize];

      尽管看起来好像做了一件合理的事,但这将得出一个错误。因为bufsize占用某块内存,所以C编译器不知道它在编译时的值。在C语言中可以选择这样书写:

        const int bufzie;

      这样写在C++中是不对的,而C编译器则把它作为一个声明,指明在别的地方有存储分配。因为C默认const是外部连接的,所以这样做是合理的。C++默认const是内部连接的,这样如果在C++中想完成与C中同样的事情,必须用extern明确地把连接改为外部连接:

        extern const int bufsize;  // Declaration only

      C中使用限定符const不是很有用的,C总是迫使程序员在预处理器里使用#define

     

      指针

       当使用带有指针的const时,有两种选择:const修饰指针指向的内容,或者const修饰在指针里存储的地址(即修饰的是变量)。

     

      指向const内容的指针

      const修饰“最靠近”它的那个。这样,如果要使正指向的元素不发生改变,得写一个像这样的定义:

        const int *u;

      从标识符开始,是这样读的:u是一个指针,它指向一个const int。这里不需要初始化,因为u可以指向任何标识符(也就是说变量u不是一个const),但它所指的值是不能改变的。

        int const *v;

      并非所有的人都很肯定地认为:应该读成,“v是一个指向intconst指针”。然而,实际应读成“v是一个指向恰好是constint的普通指针”,效果与前面定义的一样。为使程序更具有可读性,应该坚持用第一种形式。

     

      const指针变量

      使指针本身成为一个const指针,必须把const标明的部分放在*的右边,如:

        int d = 1;
        int *const w = &d;

      现在它读成“w是一个指针,这个指针是指向intconst指针”。因为指针本身现在是const指针,编译器要求给它一个初始值,这个值在指针生命周期内不变。然而要改变它所指向的值是可以的,可以写:

        *w = 2;

      字符数组的字面值

      有人可以写:

      char *cp = “howdy”;

      编译器将接受它而不报告错误。从技术上讲,这是一个错误,因为字符数组的字面值(在这里是”howdy”)是被编译器作为一个常量字符数组建立的,所引用该字符数组得到的结果是它在内存里的首地址。修改该字符数组的任何字符都会导致运行时错误。

      如果要修改字符串,就要把它放在一个数组中:

      char cp[] = “howdy”;
      函数参数和返回值

      返回const值

      对于内部类型来说,按值返回的是否是一个const,是无关紧要的,所以按值返回一个内部类型时,应该去掉const,从而不使客户程序员混淆。

      当处理用户定义的类型时,按值返回常量是很重要的。如果一个函数按值返回一个类对象为const时,那么这个函数的返回值不能是一个左值(即它不能被赋值,也不能被修改),如:

        class X {
            int i;
        public:
            X(int ii = 0);
            void modify();
        };
        
        X::X(int ii)  { i = ii; }
        
        void X::modify() { i++; }
        
        X f5() {
            return X();
        }
        
        const X f6() {
            return X();
        }
        
        void f7(X &x) {
            x.modify();
        }
        
        int main() {
            f5() = X(1);        // OK
            f5().modify();        // OK
            //! f7(f5());        // causing warning
            // Causes complie-time errors
            //! f6() = X(1);
            //! f6().modify();
            //! f7(f6());
            
            return 0;
        }

      f5()返回一个非const X对象,然而f6()返回一个const X对象。仅仅是非const返回值能作为一个左值使用,因此,当按值返回一个对象时,如果不让这个对象作为一个左值使用,则使用const很重要。

      当按值返回一个内部类型时,const没有意义的原因是:编译器已经不让它成为一个左值(因为它总是一个值而不是一个变量)。仅当按值返回用户定义的类型对象时,才会出现上述问题。

     

      传递和返回地址

        const int *const w() {
            static int i;
            return &i;
        }
    
        int *ip2 = w();    // Not OK
        const int *const ccip = w();    // OK
        const int *cip2 = w();  // OK

      编译器拒绝把函数w()的返回值赋给一个非const指针,而接受一个const int* const,但令人奇怪的是它也接受一个const int *,这不是与返回类型恰好匹配的。因为这个值(包含在指针中的地址)正被拷贝,所以自动保持这样的约定:原始变量不能改变。

     

      

      const对象只能调用const成员函数。

      const成员函数可以被const对象和非const对象调用。不修改数据成员的任何函数都应该把它们声明为const,这样它可以和const对象一起使用。

     

      可变的:按位const和按逻辑const

      如果想要建立一个const成员函数,但仍然想在对象里改变某些数据,这时该怎么办呢?下面是一个例子:

        class Y    {
            int i;
        public:
            Y();
            void f() const;
        };
    
        Y::Y() { i = 0; }
    
        void Y::f() const {
            //! i++;    // Error  -- const member function
            ((Y *)this)->i++;    // OK: cast away const-ness
            
            // Better: use C++ explicit cast syntax:
            (const_cast<Y *>(this))->i++;
        }
    
        int main() {
            const Y yy;
            yy.f();        // Actually changes it!
        }

      这种方法是可行的,在过去的程序代码里可以看到这种用法,但这不是首选的技术。应当在类声明里使用关键字mutable,以指定一个特定的数据成员可以在一个const对象里被改变。

  • 相关阅读:
    工业和信息化部“十二五”规划教材:阵列信号处理及MATLAB实现
    PHP 7从零基础到项目实战
    HTML+CSS网页设计详解
    新编 中文版Maya入门与提高
    Lua游戏AI开发指南
    这么设计能热卖:融入运营思维的电商设计进阶宝典(套装上下册)
    WCF初探-1:认识WCF
    用微信二维码登录自己的网站
    01-C语言基本知识
    「OC」内存管理
  • 原文地址:https://www.cnblogs.com/sheshiji/p/3428096.html
Copyright © 2020-2023  润新知