c++ const 和 static用法

转载-----http://hi.baidu.com/ron_room/item/9979b987d2b0e6cbb07154c0

一、const关键字


如果把const放在变量类型名前,说明这个变量的值是保持不变的,该变量必须在定义时初始化,初始化后对它进行的任何赋值都是非法的。

当指针或者引用指向一个常量时,必须在类型名前使用const标识这个指针或者引用指向的“变量”为常量,没有的话就是语法错误。如: const int x = 5;      const int* px = &x;     const int& rx = x;这样一来,直接修改x是不可能的,通过*px或者rx修改x也是不可能的。当然,这个指针还能指向其他的地方,因为指针本身并没有被标识为const的。比如,px = &y;

假如变量是一个非常量变量,而指针或者引用的类型名前使用了const,那么,可以直接修改变量,不能通过指针或者引用修改变量。

如果想让一个指针本身成为const的,就要在*后加const,即int* const p = &x;这个时候,p就不能再指向其他变量。假如x是非常量的,那它可以通过指针进行修改,因为x并没有标识为const。当然,引用天生就是const的,它必须被初始化,而且初始化后就不能再指向新的变量。比如,int x = 5; int& r = x; r = y;第二句代码不会让r指向y,而是让x的值变成了y。

如果在函数接口(参数)中使用const,和在值、指针中使用是类似的。但是,这就更难让函数返回指向这个参数对象的指针或者引用了。如果允许的话,客户代码就有可能通过别名修改常量。比如,

class Point

{

    int x, y;

public:

    Point closestPointVal(const Point& pt)

    { if( x*x+y*y < pt.x*pt.x+pt.y*pt.y)

        return *this;

    else return pt;}

    Point* closestPointPtr(const Point& pt)

    {   return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? this : &pt;}

   Point& closestPointRef(const Point& pt)

    {   return (x*x+y*y < pt.x*pt.x+pt.y*pt.y) ? *this : pt;}

};

第一个函数是返回值的,不管用不用const,都不会修改实参pt。而第二个函数可以返回指向pt实参的指针。客户代码就可以使用这个指针修改实际参数对象的状态。同样,第三个函数返回的引用也能修改实参对象的状态。假如这样的函数是合法的,那么就有可能误用,误用后如果出了错,很难弄清是这里出的错,所以编译程序直接就认为这样的函数不合法。也就是,参数对象使用了const,而返回类型没有,这是错误的。

C++提供了三种方法解决这个问题:1.参数不使用const;2.在成员函数内部,使用const_cast运算符移去const属性;3.在另外合适的地方添加const。一个真正的程序员是不会放过任何一个机会,将设计人员的设计意图传达给客户代码程序员和维护人员的。所以,假如某个参数没有被修改,就要用const来明确说明。而使用const_cast也很糟糕,不易理解。所以只能使用第三种方法,在函数返回值前加上const,接收该返回值的指针或者引用也要是const的。这样的话,返回值只能作为右值,不能作为左值。这样的返回值不能调用它所在类的成员函数。

C++提供const关键字,不是为了保证一个变量不被修改,而是为了方便编译程序和维护人员弄清楚一个实体在程序中是否被修改了。如果函数接口中声明参数为const,我们就认为这个参数不会改变,如果没有声明为const,就认为这个参数一定被改变了,而不管函数到底有没有改变这个参数。

其实,const还有一个含义,就是在函数的参数列表的“)”之后,函数体的“{”之前。这种用法说明函数不会修改目标对象的值。

二、静态类成员

每建立一个类对象实例,都会为该实例创建一个单独的数据成员集合。不管是将对象定义为有名的局部变量,有名的全局变量,或者使用new定义为未命名的动态变量,或者按值传递一个对象作为函数参数,或者从一个函数按值返回一个对象。而对于成员函数,它们的目标代码却只产生一次。因为每个程序函数都有一个隐含的参数,即this指针。

1 用全局变量作为类特性

类为对象实例提供了一个蓝图或者说是模板,类的成员变量都是这个类的所有实例的共性,类的每一个实例都有一个单独的副本。但是有时我们需要为一个类的所有实例提供共有的数据成员副本,这比在每个类对象中维护单独的副本能更有效的合理利用内存。最常见的例子就是对类的实例进行计数。假如这个计数变量count被定义在类中,在构造函数中对其加1,在析构函数中对其减1,似乎能实现计数作用。但是实际上却是做不到的。因为count是类的成员变量,每个实例都有一个count,构造函数对其加1,只是对自己的count加了1。

使用全局变量就能解决这个问题。但是将它用在大型程序中,存在一些问题,这些问题在前面都讲过了。首先在客户代码任意地方都可能使用count,增加了模块之间的依赖性。其次,全局变量名增加了重名机会。最重要的是,count作为全局变量出现时,维护人员不能很好的了解它是做什么的,除非看注释或者查阅大量其他代码。

2 static的含义

static和const一样很复杂。

首先,将static用于一个全局变量,是说明该全局变量只对定义在同一个文件中的函数可见。即使在另一个文件中使用了extern,另一个文件中的函数也不能访问这个全局变量。

static的第二个含义是用于一个函数前,说明该函数只能在同一个文件中调用。

第三个含义是static用于函数的局部变量。它表明该变量的值不会因为函数终止而消失,它会被保存下来。再次调用该函数时,这个保存下来的值会用来初始化该变量。

第四个含义就是用于类的成员变量。它表明对类的所有对象,这个数据成员都只有一个实例。这个实例被所有对象共有。static的成员变量可以是private、public、protected的。定义和访问的语法也和其他数据成员一样。

3 静态数据成员的初始化

静态数据成员在类规格说明外部被初始化,这一点和全局变量相似。但是全局变量可以被隐式地初始化为0,而静态成员变量作为类的数据成员,必须被显式地初始化,因为类的所有的数据成员都必须被显式初始化。赋值和初始化存在着重要的区别,如果类型名后紧跟着变量名,就是初始化,如果变量名前没有类型名,就是赋值。初始化对于公共的和非公共的静态数据成员是合法的,但是对非公共的静态数据成员进行赋值却是不合法的。比如,如果count是private的,那么Point::count = 0就是错误的。

静态数据成员只能被初始化一次,所以它应该和其他的成员函数的定义一起放在.cpp文件中,而不能放在头文件中。一个静态数据成员不能是联合的成员,也不能是位域的类。联合和位域都表示属于某个特定对象的特殊内存用途,而静态数据成员却不属于特定对象,它属于整个类。

4 静态成员函数

static的第五种含义是用在类的成员函数前,表明这个函数不访问非静态数据成员,它只能访问它的参数、类的静态数据成员、全局变量。注意到静态成员函数访问的三种类型的数据,都不是描述对象状态的。而在函数参数列表后面使用const表明该函数不会修改该函数的目标对象的数据成员。所以,一个静态成员函数没有必要在参数列表之后添加一个const。

静态成员函数可以通过对象来调用,也可以直接使用类名来调用。因为它是类的特性,而不是对象的性质。

定义类的某个数据成员为静态变量,以表明此全局数据逻辑上属于该类。定义类的成员函数为静态函数,表明此全局函数逻辑上属于该类,而该函数只对静态数据、全局数据、参数进行操作。

你可能感兴趣的:(c++,学习之路)