第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对象里被改变。

原文地址:https://www.cnblogs.com/sheshiji/p/3428096.html