c++中的const

来源:互联网 发布:r语言编程合集 编辑:程序博客网 时间:2024/05/16 10:42
 

C++const总结

:对于基本声明

   1.const int r=100;

   //标准const变量声明加初始化,因为默认内部连接所以必须被初始化,其作用域

   为此文件,编译器经过类型检查后直接用100在编译时替换.

   2.extend const int r=100;

   //const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行

   初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义.

   3.const int r[]={1,2,3,4};

   struct S {int a,b;};

   const S s[]={(1,2),(3.4)};

   //以上两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中

   的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式

:对于指针

   1.const int *r=&x;

   //声明r为一个指向常量的x的指针,r指向的对象不能被修改,但他可以指向任何

   地址的常量.

   2.int const *r=&x;//与用法1完全等价,没有任何区别。

   3.int * const r=&x;

   //声明r为一个常量指针,他指向x,r这个指针的指向不能被修改,但他指向的地址

   的内容可以修改.

   4.const int * const r=&x;

   //综合1,3用法,r是一个指向常量的常量型指针.

:对于类型检查

   可以把一个非const对象赋给一个指向const的指针,因为有时候我们不想从这个

   指针来修改其对象的值,但是不可以把一个const对象赋值给一个非const指针,

   因为这样可能会通过这个指针改变指向对象的值,但也存在使这种操作通过的合

   法化写法,使用类型强制转换可以通过指针改变const对象:

   const int r=100;

   int *ptr=const_cast<int*>(&r);//C++标准,C语言使用:int* ptr =(int*)&r;

:对于字符数组

   char * name = "china";

   这样的语句,在编译时是能够通过的,但是"china"是常量字符数组,任何想修改

   他的操作也能通过编译但会引起运行时错误,如果我们想修改字符数组的话就要

   使用char name[]="china";这种形式.

:对于函数

   1.void Fuction1(const int r);

   //此处为参数传递const,意义是变量初值不能被函数改变

   2.const int Fuction1(int);

   //此处返回const,意思指返回的原函数里的变量的初值不能被修改,但是函数

   按值返回的这个变量被制成副本,能不能被修改就没有了意义,它可以被赋给任何

   const或非const类型变量,完全不需要加上这个const关键字.但这只对于内部

   类型而言(因为内部类型返回的肯定是一个值,而不会返回一个变量,不会作为左

   值使用),对于用户自定义类型,返回值是常量是非常重要的,见下面条款3

   3.Class CX; //内部有构造函数,声明如CX(int r =0)

   CX Fuction1 () { return CX(); }

   const CX Fuction2 () { return CX(); }

   如有上面的自定义类CX,和函数Fuction1()Fuction2(),我们进行如下操作时:

   Fuction1()=CX(1); //没有问题,可以作为左值调用

   Fuction2()=CX(1); //编译错误,const返回值禁止作为左值调用.因为左值

              把返回值作为变量会修改其返回值,const声明禁止这种修改.

   4.函数中指针的const传递和返回

   int F1 (const char * pstr);

   //作为传递的时候使用const修饰可以保证不会通过这个指针来修改传递参数的

   初值,这里在函数内部任何修改*pstr的企图都会引起编译错误.

   const char* F2();

   //意义是函数返回的指针指向的对象是一个const对象,它必须赋给一个同样是指

   const对象的指针.

   const char* const F3();

   //比上面多了一个const,这个const的意义只是在他被用作左值时有效,它表明这

   个指针除了指向const对象外,它本身也不能被修改,所以就不能当作左值来处理.

   5.函数中引用的const传递

   void F1 (const X& px);

   //这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁

   止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,

   然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.

   另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性,

   且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const

   传递能够捕捉到这个家伙.

:对于类

   1.首先,对于const的成员变量,只能在构造函数里使用初始化成员列表来初始化,

   试图在构造函数体内进行初始化const成员变量会引起编译错误.初始化成员列表

   形如:X::X(int ir):r(ir){} //假设r是类Xconst成员变量

   2.const成员函数.提到这个概念首先要谈到const对象,正象内置类型能够定义

   const对象一样(const int r=10;),用户自定义类型也可以定义const对象

   (const X px(10);),编译器要保证这个对象在其生命周期内不能够被改变.如果

   你定义了这样的一个const对象,那么对于这个对象的一切非const成员函数的调

   ,编译器为了保证对象的const特性,都会禁止并在编译期间报错.所以如果你想

   让你的成员函数能够在const对象上进行操作的话,就要把这个函数声明为const

   成员函数.

   假如f()是类中的成员函数的话,它的声明形如:

   int f()const;

   //const放在函数的最后,编译器会对这个函数进行检查,在这个

   函数中的任何试图改变成员变量和调用非const成员函数的操作都被视为非法

   注意:类的构造和析构函数都不能是const函数.

   3.建立了一个const成员函数,但仍然想用这个函数改变对象内部的数据.这样的

   一个要求也会经常遇到,尤其是在一个苛刻的面试考官那里.首先我们要弄清楚考

   官的要求,因为有两种方法可以实现,如果要求不改变原来类的任何东西,只让你从

   当前这个const成员函数入手,那么你只有使用前面提到的类型强制转换方法.实例

   如下:

   //假如有一个叫做X的类,它有一个int成员变量r,我们需要通过一个const成员函

   f()来对这个r进行++r操作,代码如下:

   void X::f()const

   {const_cast<X*>(this)->++r; } //通过this指针进行类型强制转换实现

   另外一种方法就是使用关键字:mutable.

   如果你的成员变量在定义时是这个样子的:mutable int r;

   那么它就告诉编译器这个成员变量可以通过const成员函数改变.编译器就不会再

   理会对他的检查了

 

第一个函数:

 

const Rational& operator* (const Ratinal& lhs,cibst Ratuibak& rhs)

{

    Ratinal result(lhs.n*rhs.n, lhs.d*rhs.d);//构造一个局部对象!

    return result;

}

 

上述函数内部构造了一个局部对象,返回值将以常型引用指向之!因此,这函数表达了一个错误的语意,以引用去指向不存在的东西!

 

在我的VC7.1编译器上会生成一个警告错误:"warning C4172: 返回局部变量或临时变量的地址!"

在我的Gcc编译器上也会生成一个警告错误:

warning: reference to local variable `_f' returned

针对上述两个编译器,该函数可以通过编译链接执行!

 

BC6.0编译器产生编译错误:

[C++ Error].cpp(30): E2363 Attempting to return a reference to local variable '_f'

该函数无法通过编译!

 

VCGcc上从调试器中观察该函数中的局部对象在该函数结束时已经析构,且没有临时对象的产生,此时使用该函数的返回值(const Rational& )去寻址期望的对象有不可预见的结果!

 

请参考如下程序:

 

class Fish

{    

       long x;

public:

       Fish(const long& _x = 0) : x(_x)

       {

              cout << "建构Fish" << endl;

       }

 

       ~Fish()

       {

              cout << "析构Fish" << endl;

       }

       void show(void)const

       {

              cout << "成员x" << x << endl;

       }

};

 

const Fish& Testf()

{

    Fish _f(8);//'8'初始化建构局部对象变量;

 

    //期望如下语句返回成员x值是8的局部对象_f:

 

    return _f;//生成错误warning C4172: 返回局部变量或临时变量的地址

   

    //_f在此被析构!

}

 

int main()

{

   const Fish& _fx = Testf();//_fx期望的常型引用返回值!

   _fx.show(); //期望输出"成员x8",实际输出x值为不可预见值!

      

   _PAUSE;

   return 0;

}

 

综上所述,上述函数表达了一个错误的语意,以引用去指向不存在的东西(空引用,这是违反语言规则的!),这应是编译器的bug(而且指向的的确是个无用的东东)!

 

第二个函数:

 

inline const Rational operator* (const Ratinal& lhs,cibst Ratuibak& rhs)

{

    return Ratinal(lhs.n*rhs.n, lhs.d*rhs.d);

}

 

这函数与第一个相比有明显不同,如我们都看见的有内联声明(让编译器去看吧!),这个函数构造一个匿名对象,并以此作为返回值。

 

它代表了一个的正确C++语意之一:返回一个值对象!各种编译器均能对此有良好的支持!

 

请参考如下程序:

 

class Fish

{    

       long x;

public:

       Fish(const long& _x = 0) : x(_x)

       {

              cout << "建构Fish" << endl;

       }

 

       Fish(const Fish& _f)

       {

              x = _f.x;

              cout << "考贝建构Fish" << endl;

       }

       ~Fish()

       {

              cout << "析构Fish" << endl;

       }

       void show(void)const

       {

              cout << "成员x" << x << endl;

       }

};

 

const Fish Testf()

{

    Fish _f(8);//

    return _f;//这儿将以_f为初值考贝建构一个Fish临时对象!

}

 

int main()

{

   const Fish& _f = Testf();

   _f.show();

      

   _PAUSE;

   return 0;

}

 

所以第二个函数:

 

inline const Rational operator* (const Ratinal& lhs,cibst Ratuibak& rhs)

{

    return Ratinal(lhs.n*rhs.n, lhs.d*rhs.d);

}

 

有语意正确,编译器行为良好的优点!

 

关于其中提到的临时对象,我刚在论坛上参加了讨论,现针对上述程序摘略如下:

 

Testf()是返回一个临时对象,并且以引用绑定这个临时对象,这样一来,根据C++标准对临时对象生命期的规定二,Testf()产生的那个临时对象必须要等绑定它的引用_f的生命期结束,它才可以被析构!

 

祥情请大家见《函数返回值问题 》的讨论,上有精采的讨论,在此从略!

原创粉丝点击