c++常量详解
概念 常量是存放固定且不可变值的,一旦确定初始值则在程序其它地方不可改变,所以const对象必须初始化。常量一般使用const关键字来修饰。 const 对象可以大致分为三类: 1. const int a const int a =10; 这两种格式是完全相同的。也就是说const 与int哪个写前都不影响语义。有了这个概念后,我们来看这两个家伙:const int * pi与int const * pi ,它们的语义有不同吗? 你只要记住一点,int 与const 哪个放前哪个放后都是一样的,就好比const int n;与int const n;一样。也就是说,它们是相同的。 2. const int * p 前面已经说了 const int * p与int const * p 是完全一样的。 我们根据下面的例子看看它们的含义。 int a =30; int b =40; const int * p=&a; p=&b; //注意这里,p可以在任意时候重新赋值一个新内存地址 b=80; //想想看:这里能用*pi=80;来代替吗?当然不能 printf( “%d”,*p ) ; //输出是80 语义分析: p的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*p来修改b的值。 首先const 修饰的是整个*p(注意,是*p而不是p)。所以*p(p指向的对象)是常量,是不能被赋值的(虽然p所指的b是变量,不是常量)。 你可能会疑问:那又如何用const 来修饰pi呢?其实,你注意到int * const pi中const 的位置就大概可以明白了。请记住,通过格式看语义。 我们看看下面的定义。 3. int* const p 这里的const修饰的p,而不是上面的*p,我们根据下面的例子来分析: int a=30; int b=40; int * const p=&a; //p=&b; 注意这里,p不能再这样重新赋值了,即不能再指向另一个新地址。 b=80; //这里能用*p=80;来代替吗?可以,这里可以通过*p修改a的值。 //请自行与前面一个例子比较。 printf( “%d”,*p ) ; //输出是80 *p=100; printf( “%d”,a ) ; //输出是100 语义分析: 1). p因为有了const 的修饰,所以只是一个指针常量:也就是说p值是不可修改的(即p不可以重新指向b这个变量了)。 2). 整个*p的前面没有const 的修饰。也就是说,*p是变量而不是常量,所以我们可以通过*p来修改它所指内存a的值。 总之一句话,这次的p是一个指向int变量类型数据的指针常量。 4.补充三种情况。 这里,我再补充以下三种情况。其实只要上面的语义搞清楚了,这三种情况也就已经被包含了。不过作为三种具体的形式,我还是简单提一下吧! 情况一:int * pi指针指向const int n常量的情况 const int n1=40; int *pi; pi=&n1;//这样可以吗?不行,VC下是编译错。const int 类型的n1的地址是不能赋值给指向int类型地址的指针pi的。否则pi岂不是能修改n1的值了吗! pi=(int* ) &n1; // 这样可以吗?强制类型转换可是C所支持的。 VC下编译通过,但是仍不能通过*pi=80来修改n1的值。去试试吧!看看具体的怎样。 情况二:const int * pi指针指向const int n1的情况 const int n1=40; const int * pi; pi=&n1;//两个类型相同,可以这样赋值。n1的值无论是通过pi还是n1都不能修改的。 情况三:用const int * const pi申明的指针 int n; const int * const pi=&n; //你能想象pi能够作什么操作吗?pi值不能改,也不能通过pi修改n的值。因为不管是 //*pi还是pi都是const的。 5. 常量引用 int a = 10; int& p1 = a;//正确 int& p2 = 2;//错误,需要引用左值 const int& p3 = a;//正确 const int& p4 = 4;//正确 关于引用的初始化有两点值得注意: (1)当初始化值是一个左值(可以取得地址)时,没有任何问题,可以用常量引用也可以用非常量引用, 如p1,p3; 首先将值隐式转换到类型T,然后将这个转换结果存放在一个临时对象里,最后用这个临时对象来初始化这个引用变量。 如果是对一个常量进行引用,则编译器首先建立一个临时变量,然后将该常量的值置入临时变量中,对该引用的操作就是对该临时变量的操作。对常量的引用可以用其它任何引用来初始化;但不能改变。 6. 常量函数、常量引用参数、常量引用返回值 例1:bool verifyObjectCorrectness(const myObj &obj); //const reference parameter 6.1 常量函数 class Fred { public: void inspect() const; // This member promises NOT to change *this void mutate(); // This member function might change *this }; void userCode(Fred& changeable,const Fred& unchangeable) { changeable.inspect(); // OK: doesn't change a changeable object changeable.mutate(); // OK: changes a changeable object unchangeable.inspect(); // OK: doesn't change an unchangeable object unchangeable.mutate(); // ERROR: attempt to change unchangeable object } 7. 在类中允许存在同名的常量函数和非常量函数,编译器根据调用该函数的对象选择合适的函数 #include <iostream> using namespace std; struct A { void f() const { cout<<"const function f is called"<<endl; } void f() { cout<<"non-const function f is called"<<endl; } void g() { cout<<"non-const function g is called"<<endl; } }; void main() { A a; const A& ref_a = a; a.f(); //calls void f(),输出:non-const function f is called<br> ref_a.f();//calls void f () const,输出:const function f is called a.g(); //ok,normal call <br> ref_a.g(); //error,const object can not call non-const function } 8. const关键字不能用在构造函数与析构函数中。因为构造函数的目的是初始化域值,因此它必须更改对象,析构函数同理 6.2 常量引用参数 本例中,一个myObj类型的对象obj通过引用传入函数verifyObjectCorrectness。为安全起见,使用了const关键字来确保函数verifyObjectCorrectness不会改变对象obj所引用的对象的状态。此外,通过声明参数常量,函数的使用者可以确保他们的对象 不会被改变,也不必担心在调用函数时带来副作用。以下代码试图对声明为常量引用的形参进行修改,从而不会通过编译! #include <iostream> using namespace std; class Test { public: void f(const int& arg); private: int value; }; void Test::f(const int& arg) { arg=10; //试图修改arg的值,此行将引起编译器错误 //error C2166: l-value specifies const object cout<<"arg="<<arg<<endl; value=20; } void main() { int i=7; Test test; test.f(i); cout<<"i="<<i<<endl; } 6.3 常量引用返回值 如果你想从常量方法(函数)中通过引用返回this对象的一个成员, 你应该使用常量引用来返回它,即const X& class Person { public: const string& name_good() const; // Right: the caller can't change the name string& name_evil() const; // Wrong: the caller can change the name . }; void myCode(const Person& p) // You're promising not to change the Person object { p.name_evil() = "Igor"; // but you changed it anyway!! } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |