《认清C++语言》---谈谈const
const类型修饰符可以:
1) 用来说明符号常量和常数组(必须同时进行初始化,以后不能再更新)
C++中,用关键字const修饰的标识符称为符号常量,或称为const变量。
常量在使用前必须先定义同时初始化 ,例如:
const double pi = 3.1415926或
double const pi = 3.1415926;
通常情况下const同预处理器#define一样只是将所赋值存入编译器的符号表中(符号表仅仅在编译时存在,在编译过程中编译器将程序中的名字与它在符号表中对应的数值作简单的替换),在使用时进行值替换,并不为const创建存储空间。
C++中const默认为内部连接(C中const默认是外部连接),也就是说const仅能被它所定义过的文件访问,除非用extern声明:extern const int a;这表示const的定义在其他的什么地方,这里仅仅是一个声明,但是这样的做法使const使用了外部连接,也就是说上面的extern强制进行了对const的存储空间的分配。
(补充说明一个内部连接和外部连接。内部连接意味着只对正在编译的文件创建存储空间,别的文件可以使用相同的标示符的全局变量,编译器不会发现冲突;外部连接意味着为所有被编译过的文件创建一片单独的存储空间,一般全局变量和函数名的外部连接通过extern声明,可以通过其他文件访问)
如果强制取const常量的地址,或者用extern来声明外部的const变量,C++编译器就要为const变量分配存储空间了。
由于用const修饰的变量的值不能被改变,只能被读取,因此,在声明处必须初始化变量,除非这个变量是用extern修饰的外部变量,例如:
const int d; //err.
extern const int d; //ok.此处只是声明d变量,该变量是在其他文件中定义的
常数组:int const c[10] = {1,2,3,4,5,6,7,8,9,10};
因为在类对象里进行存储空间分配时,编译器不能知道const的内容是什么,所以不能把const常量作为编译期间的常量,例如:
ACEClass AC
{
const size = 100; //非法
int array[size]; //非法
};
在类里const常量的意思是“在这个特定对象的生命期内,而不是对于整个类来说,这个值是不变的”。所以,要建立一个可以用在常数表达式中的类常量时,我们使用一个不带实例的无标记的enum,例如:
ACEClass AC
{
enum {size = 100};
int en[size];
};
使用enum是不会占用对象中的存储空间的,枚举常量在编译时被全部求值。
2) 用于说明常量对象(定义时同样必须进行初始化,以后不能再更新)
定义格式如下:<类名> const <对象名>或const <类名><对象名>,如:
ACEPoint const ap(12, 200);
对于一个常量对象来说,只能调用常量成员函数(因为如果可以调用普通成员函数的话,就很可能通过普通成员函数改变对象的数据成员)。对于上面这个常量对象来说,如果改变它的数据成员或者调用它的一个非常量成员函数,编译器都会给出出错信息。
const对象只能调用const成员函数,一个普通对象同样可以调用const成员函数,因此const成员函数更具一般性(能同时被const和非const对象调用撒), 因此,一般对于不修改数据成员的函数来说,应当将其声明为常量成员函数,这样就可以被常量对象所调用了。
使用常对象可以实现对数据共享的保护,同时常成员函数又打开了1个常对象的对外的接口,使保护和开放统一了起来。
3) 用于说明常量成员函数和常数据成员
常成员函数:使用const进行说明的成员函数。
常成员函数说明格式如下:<类型说明符> <函数名>(<参数表>) const;其中const是加在函数说明后面的类型说明符,它是函数类型的一个组成部分,因此在函数实现部分也要带上const关键字。
常成员函数不能更新对象的数据成员,也不能调用该类中非const成员函数。
类的成员函数的原型后加上const是为了防止修改对象的数据成员的值,如:
class ACEClass
{
public: void setNum(int nu) const;
private: int num;
};
此时,常量成员函数setNum()的函数体中不能修改num的值,如:
void ACEClass::setNum(int nu)
{
num = nu; //err.不能修改成员num的值
}
但是,有时我们在运用const成员函数是,遇到有需要改变的数据成员,这时可以用mutable来对该数据成员进行特别的指定:
class ACEClass
{
public: void setNum(int nu) const;
private: mutable int num;
};
void ACEClass::setNum(int nu) {num = nu}; //ok.
常数据成员:因为const类型对象必须被初始化,并且不能更新,因此,在类中说明const数据成员后,在构造函数中只能通过成员初始化列表的方式来对数据成员进行初始化(理由是常量数据成员构造函数调用之前就必须有确定的数值,否则可以在构造函数中对其进行赋值或修改,这与常量数据不可修改相悖)。
在类的私有(Private)数据成员前加上const,则该私有数据成员就称为常量数据成员。
注意:1)定义一个类就是定义了一种数据类型,而不是定义了某个对象,在类中定义数据成员时不能直接初始化,例如:
class ACEClass
{
private:
const int size = 10; //err.
......
};
2)类中常量数据成员的初始化只能采用成员初始化列表的形式来初始化其数值,而不能采取赋值的方法,如下定义是错误的:
class ACEClass
{
private:
const int size;
public:
ACEClass() {size = 10;} //err.
};
正确的初始化代码如下:
Class ACEClass //增加了一个静态的常数据成员和一个常引用
{
private:
const int size; //常数据成员
static const int height; //静态常数据成员
public :
const int &ref; //常引用
};
const int ACEClass::height = 20; //静态常量数据成员在类外初始化
ACEClass::ACEClass() : size(10), ref(size){}
4) 用于说明常指针和常引用
常指针:
1)指向常量的指针:(意味着指向的元素是不能被改变的,定义格式:const Type *x;)
char str[] = “艾斯”;
const char *ptr = str; //以后*ptr不可改变,但ptr可以指向别的内存地址
定义指向常量的指针只限制指针的间接访问操作,而不限制指针的直接访问操作。
2)常量指针:(定义格式:Type* const x;)
char str[] = “艾斯”;
char* const ptr = str; //以后ptr不可改变,但*ptr可以指向别的内存地址
常引用:
使用const修饰符说明的引用称为常引用,被引用的对象不能被更新,定义格式如下:
Const <类型说明符> &<引用名>,例如:const double &x;
在实际应用中,常指针和常引用往往用作函数的“形参”,这样的参数称为常参数。使用常参数表明该函数不会更新某个参数所指向的或引用的对象,这样,在参数传递过程中就不需要执行拷贝初始化构造函数了。
5)用于说明按值传递的函数返回值
1)对于内部数据类型(int,float...)来说,函数按值返回的是否是一个const是无关紧要的,因为编译器始终不会让它成为一个左值。也就是说,此时用const来限定是多余的,如:
const int ace() {return 10;} //多余滴
void main() {int a = ace();}
2)但是对于用户自定义类型来说,按值返回常量就很有意义了。这时函数返回值不能被修改或直接赋值;只有非const返回值才能作为一个左值使用,但这往往是没有意义的,因为函数返回值在使用时通常保存为一个临时量,该临时量被作为左值使用并修改后,编译器会在函数返回时将临时量清除,这样就丢失了所有的修改。
因此,一般函数返回值都是作为一个指针或引用来实现的。
虽然const修饰的符号生存期间保持不变,但有时我们仍然想改变数据时,可以进行强制类型转换,这里的转换可以用传统的C下的方式,在C++中,推荐用const_cast,然后就可以对其进行修改了。
posted on 2010-04-18 12:53 android开发实例 阅读(349) 评论(0) 编辑 收藏 举报