C++ 第二章(数据类型)之四

来源:互联网 发布:svn http nginx 编辑:程序博客网 时间:2024/05/06 06:22
常量const允许我们在变化和不变之间划清一条界限.
const设计的最初动机就是取代预处理器#define进行值替代.从此其应用范围包括指针,函数变量,返回类型,类对象以及成员函数.
const应用1: 值替代
C语言中典型使用预处理器进行值替代典型的用法:
#define BUFSIZE  100;
BUFSIZE 是一个名字,它不占用存储空间而且可以放在一个头文件中,目的是为使用它的所有的编译单元提供一个数值.
特点:用C语言进行程序设计的时候,预处理器可以不受限制的建立宏并且用它来代替值.因为预处理器只做文本替换,它既没有类型检查的思想,也没有类型检查的工具,就为其代替的数混乱产生伏笔.
const的出现就是为了让#define有更加安全的保证.
头文件里面的
const
与#define一样,使const必把const放到头文件里.这样通过包含头文件,可以把const定义单独放在一个地方并且把它分配给一个编译单元.
C++中的const默认的是内部连接,也就是说:const仅在const被定义过的头文件里面才是可见的,而在连接时不能被其他变异单元看到。
当定义一个const常量的时候,必须赋值给他,除非用extern说明:extern const bufsize
虽然上面的extern强迫编译器进行了存储空间分配,但是C++编译器通常不为const分配空间的,相反把这个定义保存在它的符号表里面,当const使用的时候,他在编译器时会进行常量折叠。
当然,对于复杂的机构来讲编译器是要分配存储的,这种情况下,编译器建立存储,这样会防止常量折叠。这就是为什么const必须v默认是内部连接,即连接仅在特别编译单元的原因。否则,由于众多的const在多个cpp文件内分配存储,容易引起连接错误,连接程序在多个对象文件里面看到同样的定义就会“抱怨”。然而,因为const默认内部连接,所以连接程序vbuhui 跨越 编译单元连接那些定义,因此不会冲突。对于在大量场合使用的内部数据类型包括常量表达式,编译器都会进行常量折叠。
const安全性:
const的作用不限于在常量表达式里代替#defines。如果用运行期间产生的值初始化一个变量而且知道在那个变量寿命期间内它是不变的,用const限定该变量,程序设计中这是一个很好的做法。
实际上,如果想一个值不变,就应该使之成为常量(const).这不仅为防止意外的更改提供安全措施,也是消除存储和内存操作,使编译器产生的代码更有效.
集合:
const可以用集合,但是编译器不能把一个集合放在他的符号表里面,所以必须分配内存,在这种情况下,const意味着不能改变的一块儿内存.然而其值在编译期间不能被使用,因为编译器在编译时不需要知道存储内容.

const应用2:

引用对于变量来说是变量的别名。对其操作就是对所代表的变量操作。

I: 指针

我们还可以使指针成为conost,当处理const指针时,编译器仍将努力阻止存储分配并进行常量折叠.但是在这种情况下,这些特征似乎很少用,更重要的是,如果以后程序员想在代码中改变这种指针的使用,编译器会给出通知,这样大大的增加了安全性.
当使用带有指针的const时,有两种做法:或者const修饰指针正指向的对象或者const修饰存储在指针本身的地址里.

1: 指向const的指针
const int* x;                          //其正指向的元素不发生变化

x是一个指针,它指向一个const int.这里不需要初始化,因为说x可以指向任何东西,但是它指向的东西是不能被改变的.
int const* x;
x是一个指向恰好是const的int普通指针.
2:const指针
使指针本身成为一个const指针,必须把const标明的部分放在*右边。
int d;
int* const x = &d;                         //指针本身不变
x是一个指针,这个指针是指向int的const指针。

注意: 不管在任何的时候,在同一行存放一个指针的定义,并且在定义的地方初始化每个指针.正是因为这一点才把 "*" 赋于数据类型之上.事实上"*"是与标识符结合的.

II: 函数参数和返回值
如果以值传递对象时候,对于用户来讲,用const来讲是没有任何意义的。如果以常量返回用户定义类型的一个对象的值,这意味着返回值不能被修改。如果传递或返回地址,const将保证地址内容不会被改变。
A:传递const值
如果是以值传递的,可以用const限定函数参数。参数是以值传递的,因此要立即制作变量的副本(这个约定对于用户来说是隐藏的)。
在函数里,const有这样的定义:参数不能被改变。所以它其实是函数创建者的工具,而不是函数调用者的工具。
在函数内部调用const限定参数优于在参数表里面用const限定参数。
B:返回const值
返回一个内部数据类型没有必要用const修饰,因为编译器不会让它成为一个左值(因为它总是一个数值而不是一个变量)。所以如果不想让自定义类型作为左值就要使用const修饰。
所有的临时变量都是自动成为常量。
C:传递和返回地址
为了防止修改指针(引用)的初值,使用const修饰。事实上,无论何时我们使用一个指针,我们都使用const来修饰它。
编译器不允许把存放在const指针里面的地址来建立一个非const指针。
D:标准值传递
C语言中,值传递时很普通,但是要传递地址的时候就要用指针了。
在C++中传递一个参数的时候,首先想到的是引用传递,而且是通过常量(const)引用的传递。如果通过常量const引用来传递,这意味着函数将不能够改变该地址的内容,其实和值传递相同。
由于引用语法的原因,传递一个临时变量给带有一个引用的函数是可能的,但是不可以传递一个临时对象给带有一个指针的函数---因为它必须清楚地带有地址,所以会出现以下情况:一个总是常量的临时变量,他的地址可以被传递给一个函数。
例子说明:

IV类
A:在一个类里面使用const的意义是:在这个对象寿命期内,这是个常量。然而对于这个常量来讲,一个类的不同常量可以含有一个不同的数值。
在一个类里面建立一个const时候,不能够给他初值。这个初始化的工作必须建立它的地方初始化,在构造函数的主题里面,const必须已经初始化了(因为在函数里里面他何以在任何地方初始化,这样无法阻止在构造函数的不同的地方改变const数值),所以必须在构造函数的初始化列表里面初始化,其行为发生在构造函数的任何代码执行之前。
class fred
{
  const size;
public:
   fred();
   fred(int i);
};

fred::fred() : size(100)  {}
fred::fred(int i) : size(i)  {}

B:编译期间的常量
类中的const意思是在这个特定对象寿命期间,而不是对于这个类来说是const.。如果要保证类中的常量,而且是编译期间求职需要是enum;
class bunch
{
  enum { size = 100; }
  int i[size];
};

C:const对象和成员函数
const对象和const成员函数是密不可分的。如果声明一个成员函数为const函数,则等于告诉编译器可以为一个const对象调用这个函数。一个没有特别声明为const的成员函数被看成是将要修改对象中数据成员的函数,而且编译器不会被允许一个const对象调用这个函数。
同inline类似,const标识的函数需要在定义时候强调
//
#include<iostream>
#include<stdlib>
#include<time>

using namespace std;

class quoter
{
    int  lasquote;
public:
   quoter();
   int Lastquote() const;
   const char* quote();
};

quoter::quoter()
{
    lasquote = -1;
    time_t t;
    srand( (unsigned)time(&t) );  //Seed generator
   
}

int quoter::Lastquote() const
{
    return lasquote;
}

const char* quoter::quote()
{
    static const char* quotes[] = {
        "Are we having fun yet?",
        "Doctors always know best",
        "Is it ... automic"
    };
    const qsize = sizeof quotes/sizeof* quotes;
    int qnum = rand() % qsize;
    while( lasquote >=0 && qnum == lasquote )
        qnum = rand() % qsize;
    return quotes[ lasquote = qnum ];
}

int main()
{
    quoter q;
    const quoter cq;
    cq.Lastquote(); //ok
    q.Lastquote();
    // cq.quote();
    for(int i=0;i<20;i++)
        cout<<q.quote()<<endl;

    return 0;
}
按位和按成员const
如果我们想建立一个const函数,但是仍然想在对象里改变某些数据。这样我们要理解两个概念,按为consthe 按成员const的区别。
按位const的意思是对象中的每个位都是固定的,所以对象的每个位映像从不改变。
按成员const的意思是虽然整个对象从概念上讲是不变的,但是某个成员可能有变化。当编译器被告诉一个对象是const的时候,它将保护这个对象。
class y
{
  int i,j;
public:
   y() { i=j=0; }
  void f() const;
 
};

public void  y::f() const
{
  // i++;  //Error
  ( (y*)this )->j++;
}

void main()
{
   const y yy;
   yy.f(); 
}

还有一种方法是使用mutable来修饰变量来表示其在const函数里面可以改变其值。

class y
{
  int i;
  mutable int j;
public:
   y() { i=j=0; }
  void f() const;
 
};

public void  y::f() const
{
  // i++;  //Error
  //( (y*)this )->j++;
  j++;
}

void main()
{
   const y yy;
   yy.f(); 
}

说明:关键字const能够将对象,函数参数,返回值以及成员函数定义为常量,并且能消除预处理的值替代而不对预处理器产生任何影响。所有这些都为程序设计提供了非常好的类型检查形式以及安全性。使用所谓的const correctness(在可能的地方使用const)已经为项目的救星了.

原创粉丝点击