基础入门:C++中const的使用方法
1.const类型定义:指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令
2.可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。
3.编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
4.可以节省空间,避免不必要的内存分配。
例如:
#define PI 3.14159 file://常量宏
const doulbe Pi=3.14159; file://此时并未将Pi放入ROM中
……
double i=Pi; file://此时为Pi分配内存,以后不再分配!
double I=PI; file://编译期间进行宏替换,分配内存
double j=Pi; file://没有内存分配
double J=PI; file://再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
对于基本声明
1.const int r=100; //标准const变量声明加初始化,因为默认内部连接所以必须被初始化,其作用域为此文件,编译器经过类型检查后直接用100在编译时替换
2. extend const int r=100; //将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义但是如果外部想链接r,不能这样用
extern const int r=10; //错误!常量不可以被再次赋值
3. const int r[ ]={1,2,3,4};
struct S {int a,b;};
const S s[ ]={(1,2),(3.4)}; //以上两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式
但是
const int Max=100;
int Array[Max];
正确。
定义数组必须用常量,可以用const或者#define定义。 Static 虽然是编译时确定,也不能用来声明数组。
对于指针和引用
1.const int *r=&x; //声明r为一个指向常量的x的指针,r指向的对象不能被修改,但他可以指向任何地址的常量
pointer const 可以指定普通变量,用改指针不能修改它指向的对象,并不表示指向的对象是const不能被改变,例如:
int i = 10;
const int * p = &i;
*p = 11; //wrong
i = 11 ; //correct
自己的一个经验:一个具体的概念可以用范型的概念来赋值,但是一个范型的概念不能用具体的概念来赋值。
我们可以把const指针当成普通指针的父类,因为普通指针改写了const属性,而具有比const指针更多的功能。 这样的话只有父类指针可以指向子类,而子类指针不能指向父类。
2.int const *r=&x; //与用法1完全等价,没有任何区别。
3.int * const r=&x; //声明r为一个常量指针,他指向x,r这个指针的指向不能被修改,但他指向的地址的内容可以修改。
4.const int * const r=&x; //综合1、3用法,r是一个指向常量的常量型指针。
5.const double & v; 该引用所引用的对象不能被更新。
引用必须定义是初始话,而且初始化后这个引用不能指向其他的对象。但是这里加的const声明不是这个意思,它是指不能改变v引用对象本身,也就是只能调用该对象里面的const成员函数。
对于类型检查
可以把一个非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传递能够捕捉到这个家伙。
6.有一点可以注意一下
const为函数重载提供了一个参考。
class A {……
void f(int i) {……} file://一个函数void f(int i) const {……} file://上一个函数的重载……
};
关于函数overloading, 不能根据返回值类型来确定
double max( int a, int b);
int max( int a, int b);
也不能根据参数的默认值来判断
int max( int a, int b);
int max( int a, int b, int c=12);
一句话不能让编译器有多个选择就ok了
对于类
1.首先,对于const的成员变量,只能在构造函数里使用初始化成员列表来初始化,试图在构造函数体内进行初始化const成员变量会引起编译错误。初始化成员列表形如:
X:: X ( int ir ): r(ir) {} //假设r是类X的const成员变量
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成员函数入手,那么你只有使用前面提到的类型强制转换方法。实例如下
- 最新评论
