您的位置:首页 > 编程语言 > C语言/C++

C++中 static、const 应用小结 .

2012-01-29 11:23 316 查看
C++ static、const和static const 以及它们的初始化

const定义的常量在函数执行之后其空间会被释放,而static定义的静态常量在函数执行后不会被释放其空间。

static 表示的是静态的。类的静态成员函数,成员变量是和类相关的,不是和类的具体对象相关,即使没有具体的对象,也能调用类的静态成员函数,成员变量。一般的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

在c++中,static静态成员变量不能在类内部初始化。

在c++中,const常量成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。

如:

class Test

{

public:

Test(): a(0){}

enum {size1=100, size2 = 200 };

private:

const int a; // 只能在构造函数初始化列表中初始化,这个似乎很少用到???

static int b;

const static int c; // 与static const int c;相同,可以在这里定义(如果以后在类中需要使用该变量的话).

}

int Test::b = 0; // 不能以成员列表初始化,不能在定义处初始化,因为不属于某个对象。;一般我在C++的.cpp文件中初始化

const int Test:: c = 0;//注意:给静态成员变量赋值时,不在需要加static修饰。但const要加。

const 成员函数,主要的目的是防止修改对象的内容。即:int Fun() const; 不可以修改对象的数据,可以访问对象的数据,访问成员函数时,只能是const的,不能是non_const的成员函数。

static成员函数,主要的目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数的没有this指针,这导致:

1. 不能直接存取类的非静态成员变量,调用非静态成员函数

2. 不能被声明为virtual

下面是关于static,const,static const , const static成员的初始化问题。

1.类里的const成员初始化:

在一个类里建立一个const时,不能给它初值。像

class foo

{

private:

const int i = 100; // error !!!

public:

foo(){}

......

};

这样的初始化方式是不能通过编译的,因为在类对象里进行了存储空间分配,编译器不能知道const的内容是什么,所以不能把它用作编译期间的常量。这意味着对于类里的常数表达式来说,const就像它在C中一样没有作用。因此这个初始化工作必须发生在构造函数里,并且,要在构造函数的某个特别的地方。因为const必须在建立它的地方被初始化,所以在构造函数的主体里,const必须已初始化了,否则,就只有等待,直到在构造函数主体以后的某个地方给它初始化,这意味着过一会儿才给const初始化。当然,无法防止在在构造函数主体的不同地方改变const的值。

构造函数初始化表达式

class foo

{

private:

const int i = 100;

public:

foo(){......}

......

};

如果构造函数是在类外定义,则可以这样写:

class foo

{

private:

const int i;

public:

foo();

......

};

foo ::foo() : i (100) {......} // 初始化列表

2.类里的static成员初始化:

类中的static变量是属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此不能在定义对象时对变量初始化,就是不能用构造函数来初始化。其正确的初始化方式是:

<数据类型><类名> ::<静态数据成员名> = <值> ,例如

class foo

{

private:

static int i;

public:

foo();

......

};

int foo ::i = 100;

这表明:

(1)初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。

(2)初始化时不加该成员的访问权限控制符private,public等。

(3)初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。

3.类里的static const 和const static成员初始化:

在找static成员的初始化学习资料时,我发现了网上有很多关于 static const 成员,也就是全局的静态常量的初始化的资料。const成员要在构造函数初始化,而static成员在需要在在类体外初始化,那么static const 和 const static 的成员应该在哪里进行初始化呢?这两个写法又会有什么不一样吗?其实他俩都一样。在借阅《Thinking in c++》中文版188页和做了相关实验后,我认为若在类内使用的话,static const 则必须在static const 定义之处进行初始化。其他情况在类里外都行。

比如:

class Test{

public:

static const int MASK1;

const static int MASK2;

};

const int Test ::MASK1 = 0xFFFF;

const int Test ::MASK2 = 0xFFFF;

它们没有区别,虽然

一个是静态常量,

一个是常量静态,

静态都将存储在全局变量区域,其实最后结果都是一样的。

可能在不同编译器内,不同处理,但是最后结果是一样的。

下面是一个完整的例子:

#ifndef A_H_

#define A_H_

#include<iostream>

using namespace std;

class A{

private:

static int aa; //静态数据成员声明

static const int count; //静态常数据成员声明(可以在此初始化)

const int bb; //常数据成员

public:

A(int a);

static void print(); //静态成员函数

};

A::A(int a):bb(a) { //常成员的初始化

aa += 1;

}

void A::print() {

cout << "connt=" << count<< endl;

cout << "aa=" << aa << endl;

};

int A::aa = 0; //静态成员定义

const int A::count = 25; //静态常成员初始化

#endif

int main(){

A a(10);

A::print(); //通过类访问静态成员函数

a.print(); //通过对象访问静态成员

}

但是注意:将static const datamember的申明放在头文件中时,新的C++标准支持,但VC6.0却不支持

C++的const类型

  常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,

定义或说明常类型时必须进行初始化。

  一般常量和对象常量

  1. 一般常量

  一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以

用在类型说明符后。如:

  int const x=2;

  或

  const int x=2;

  定义或说明一个常数组可采用如下格式:

  <类型说明符> const <数组名>[<大小>]…

  或者

  const <类型说明符> <数组名>[<大小>]…

  例如:

  int const a[5]={1, 2, 3, 4, 5};

  2. 常对象

  常对象是指对象常量,定义格式如下:

  <类名> const <对象名>

  或者

  const <类名> <对象名>

  定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,

也可以放在类名前面。

  常指针和常引用

  1. 常指针

  使用const修饰指针时,由于const的位置不同,而含意不同。下面举两个例子,说明它们的区别。

  下面定义的一个指向字符串的常量指针:

  char * const prt1 = stringprt1;

  其中,ptr1是一个常量指针。因此,下面赋值是非法的。

  ptr1 = stringprt2;

  而下面的赋值是合法的:

  *ptr1 = "m";

  因为指针ptr1所指向的变量是可以更新的,不可更新的是常量指针ptr1所指的方向(别的字符串)。

  下面定义了一个指向字符串常量的指针:

  const * ptr2 = stringprt1;

  其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新

的。因此,

  *ptr2 = "x";

  是非法的,而:

  ptr2 = stringptr2;

  是合法的。

  所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量和定义一

个指向字符串常量的指针时,const修饰符的位置不同,前者const放在*和指针名之间,后者const放在

类型说明符前。

  2. 常引用

  使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其

定义格式如下:

  const <类型说明符> & <引用名>

  例如:

  const double & v;

  在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。

  在C++面向对象的程序设计中,指针和引用使用得较多,其中使用const修饰的常指针和常引用用得

更多。使用常参数则表明该函数不会更新某个参数所指向或所引用的对象,这样,在参数传递过程中就

不需要执行拷贝初始化构造函数,这将会改善程序的运行效率。

  下面举一例子说明常指针作函数参数的作法。

#include 

const int N = 6;

void print(const int *p, int n);

void main()

{

int array
;

for (int i=0; i cin>>array[i];

print(array, N);

}

void print(const int *p, int n)

{

cout<<"{"<<*p;

for (int i=1; i cout<<","<<*(p+i);

cout<<"}"< }

  常成员函数

  使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常

对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:

  <类型说明符> <函数名> (<参数表>) const;

其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部

分也要带const关键字。下面举一例子说明常成员函数的特征。

#include 

class R

{

public:

R(int r1, int r2) { R1=r1; R2=r2; }

void print();

void print() const;

private:

int R1, R2; 

};

void R::print()

{

cout< }

void R::print() const

{

cout< }

void main()

{

R a(5, 4);

a.print();

const R b(20, 52);

b.print();

}

  该例子的输出结果为:

  5,4

  20;52

  该程序的类声明了两个成员函数,其类型是不同的(其实就是重载成员函数)。有带const修饰符的

成员函数处理const常量,这也体现出函数重载的特点。

  常数据成员

  类型修饰符const不仅可以说明成员函数,也可以说明数据成员。

  由于const类型对象必须被初始化,并且不能更新,因此,在类中说明了const数据成员时,只能通

过成员初始化列表的方式来生成构造函数对数据成员初始化。

  下面通过一个例子讲述使用成员初始化列表来生成构造函数。

#include 

class A

{

public:

A(int i);

void print();

const int &r;

private:

const int a;

static const int b;

};

const int A::b=10;

A::A(int i):a(i), r(a)

{

}

void A::print()

{

cout< }

void main()

{

A a1(100), a2(0);

a1.print();

a2.print();

}

  该程序的运行结果为:

  100:10:100

   0:10:0

  在该程序中,说明了如下三个常类型数据成员:

  const int & r;

  const int a;

  static const int b;

  其中,r是常int型引用,a是常int型变量,b是静态常int型变量。

  程序中对静态数据成员b进行初始化。

  值得注意的是构造函数的格式如下所示:

  A(int i):a(i),r(a)

  {

  }

  其中,冒号后边是一个数据成员初始化列表,它包含两个初始化项,用逗号进行了分隔,因为数据

成员a和r都是常类型的,需要采用初始化格式。 

2.8、const成员函数

一些成员函数改变对象,一些成员函数不改变对象。

例如:

int Point::GetY()

{

  return yVal;

}

   这个函数被调用时,不改变Point对象,而下面的函数改变Point对象:

void Point:: SetPt (int x, int y)

{

  xVal=x;

  yVal=y;

}

   为了使成员函数的意义更加清楚,我们可在不改变对象的成员函数的函数原型中加上const说明:

例10-15

class Point

{

  public:

   int GetX() const; //声明常成员函数

   int GetY() const;

  void SetPt (int, int);

   void OffsetPt (int, int);

  private:

   int xVal, yVal;

};

   const成员函数应该在函数原型说明和函数定义中都增加const限定:

例10-16

int Point::GetY() const //定义常成员函数

{

  return yVal;

}

class Set {

public:

  Set (void){ card = 0; }

  bool Member(const int) const;

  void AddElem(const int);

  //...

};

bool Set::Member (const int elem) const

{

  //...

}

非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员:

   const Set s;

  s.AddElem(10); // 非法: AddElem不是常量成员函数

  s.Member(10); // 正确

   但构造函数和析构函数对这个规则例外,它们从不定义为常量成员,但可被常量对象调用(被自动调用)。它们也能给常量的数据成员赋值,除非数据成员本身是常量。

为什么需要const成员函数?

我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。其实,它还能提高程序的可靠性,已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理。

const成员函数和const对象

实际上,const成员函数还有另外一项作用,即常量对象相关。对于内置的数据类型,我们可以定义它们的常量,用户自定义的类也一样,可以定义它们的常量对象。例如,定义一个整型常量的方法为:

   const int i=1 ;

同样,也可以定义常量对象,假定有一个类classA,定义该类的常量对象的方法为:

   const classA a(2);

这里,a是类classA的一个const对象,"2"传给它的构造函数参数。const对象的数据成员在对象生存期内不能改变。但是,如何保证该类的数据成员不被改变呢?

为了确保const对象的数据成员不会被改变,在C++中,const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改,但是它没有被const关键字限定的,也不能被常量对象调用。下面通过一个例子来说明这个问题:

例10-17

class C

{

  int X;

public:

  int GetX()

  {

   return X;

  }

  void SetX(int X)

  {

   this->X = X;

  }

};

void main()

{

  const C constC;

 cout<<constC.GetX();

}

如果我们编译上面的程序代码,编译器会出现错误提示:constC是个常量对象,它只能调用const成员函数。虽然GetX( )函数实际上并没有改变数据成员X,由于没有const关键字限定,所以仍旧不能被constC对象调用。如果我们将上述加粗的代码:

   int GetX()

改写成:

   int GetX()const

再重新编译,就没有问题了。

   const成员函数的使用

const成员函数表示该成员函数只能读类数据成员,而不能修改类成员数据。定义const成员函数时,把const关键字放在函数的参数表和函数体之间。有人可能会问:为什么不将const放在函数声明前呢?因为这样做意味着函数的返回值是常量,意义完全不同。下面是定义const成员函数的一个实 例:

   class X

   {

    int i;

    public:

    int f() const;

   };

   关键字const必须用同样的方式重复出现在函数实现里,否则编译器会把它看成一个不同的函数:

   int X::f() const //注意定义的形式

   {

    return i;

   }

   如果f( )试图用任何方式改变i或调用另一个非const成员函数,编译器将给出错误信息。任何不修改成员数据的函数都应该声明为const函数,这样有助于提高程序的可读性和可靠性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: