声明或定义的格式以下:数组
const <类型说明符> <变量名> = <常量或常量表达式>; [1] <类型说明符> const <变量名> = <常量或常量表达式>; [2] [1]和[2]的定义是彻底等价的。
例如:函数
整形int(或其余内置类型:float,double,char)this
const int bufSize = 512;
或者编码
int const bufSize = 512;
由于常量在定义后就不能被修改,因此定义时必须初始化。spa
bufSize = 128; // error:attempt to write to const object const string cntStr = "hello!"; // ok:initialized const i, j = 0; // error: i is uninitialized const
非const变量默认为extern。指针
const 对象默认为文件的局部变量。要使const变量可以在其余的文件中访问,必须显式地指定它为extern。code
例如:对象
const int bufSize = 512; // 做用域只限于定义此变量的文件 extern const int bufSize = 512; // extern用于扩大做用域,做用域为整个源程序(只有extern 位于函数外部时,才能够含有初始化式)
声明或定义的格式以下:内存
const <类型说明符> <数组名>[<大小>]…… [1] <类型说明符> const <数组名>[<大小>]…… [2] [1]和[2]的定义是彻底等价的。
例如:作用域
整形int(或其余内置类型:float,double,char)
const int cntIntArr[] = {1,2,3,4,5};
或者
int const cntIntArr[] = {1,2,3,4,5}; struct SI { int i1; int i2; }; const SI s[] = {{1,2},{3,4}}; // 上面的两个const都是变量集合,编译器会为其分配内存,因此不能在编译期间使用其中的值(例如:int temp[cntIntArr[2]],这样的话编译器会报告不能找到常量表达式)
声明或定义的格式以下:
const <类型说明符> &<变量名> = …… [1] <类型说明符> const &<变量名> = …… [2] [1]和[2]的定义是彻底等价的。
例如:
const int i = 128; const int &r = i;(或者 int const &r = i;)
const 引用就是指向const 对象的引用。
普通引用不能绑定到const 对象,但const 引用能够绑定到非const 对象。
const int ii = 456; int &rii = ii; // error int jj = 123; const int &rjj = jj; // ok
非const 引用只能绑定到与该引用同类型的对象。
const 引用则能够绑定到不一样但相关的类型的对象或绑定到右值。
例如:
1.const int &r = 100; // 绑定到字面值常量 2.int i = 50; const int &r2 = r + i; // 引用r绑定到右值 3.double dVal = 3.1415; const int &ri = dVal; // 整型引用绑定到double 类型
编译器会把以上代码转换成以下形式的编码:
int temp = dVal; // create temporary int from double const int &ri = temp; // bind ri to that temporary
1.指向const 对象的指针(指针所指向的内容为常量)
声明或定义的格式以下(定义时能够不初始化):
const <类型说明符> *<变量名> …… [1] <类型说明符> const *<变量名> …… [2] [1]和[2]的定义是彻底等价的。
例如:
const int i = 100; const int *cptr = &i;
或者
int const *cptr = &i; [cptr 是指向int 类型的const 对象的指针] 容许把非const 对象的地址赋给指向const 对象的指针,例如: double dVal = 3.14; // dVal is a double; its value can be change const double *cdptr = &dVal; // ok;but can't change dVal through cdptr
不能使用指向const 对象的指针修改基础对象。然而若是该指针指向的是一个没const 对象(如cdptr),可用其余方法修改其所指向的对象。
如何将一个const 对象合法地赋给一个普通指针???
例如:
const double dVal = 3.14; double *ptr = &dVal; // error double *ptr = const_cast<double*>(&dVal); // ok: const_cast是C++中标准的强制转换,C语言使用:double *ptr = (double*)&dVal;
2.const 指针(指针自己为常量)
声明或定义的格式以下(定义时必须初始化):
<类型说明符> *const <变量名> = ……
例如:
int errNumb = 0; int iVal = 10; int *const curErr = &errNumb; [curErr 是指向int 型对象的const 指针] 指针的指向不能被修改。 curErr = &iVal; // error: curErr is const 指针所指向的基础对象能够修改。 *curErr = 1; // ok:reset value of the object(errNumb) which curErr is bind
3.指向const 对象的const 指针(指针自己和指向的内容均为常量)
声明或定义的格式以下(定义时必须初始化):
const <类型说明符> *const <变量名> = ……
例如:
const double pi = 3.14159; const double dVal = 3.14; const double *const pi_ptr = π [pi_ptr 是指向double 类型的const 对象的const 指针] 指针的指向不能被修改。 pi_ptr = &dVal; // error: pi_ptr is const 指针所指向的基础对象也不能被修改。 *pi_ptr = dVal; // error: pi is const
1.修饰函数的参数
class A; void func1(const int i); // i不能被修改 void func3 (const A &rA); // rA所引用的对象不能被修改 void func2 (const char *pstr); // pstr所指向的内容不能被修改
2.修饰函数的返回值
返回值:const int func1(); // 此处返回int 类型的const值,意思指返回的原函数里的变量的初值不能被修改,可是函数按值返回的这个变量被制成副本,能不能被修改就没有了意义,它能够被赋给任何的const或非const类型变量,彻底不须要加上这个const关键字。 [*注意*]但这只对于内部类型而言(由于内部类型返回的确定是一个值,而不会返回一个变量,不会做为左值使用,不然编译器会报错),对于用户自定义类型,返回值是常量是很是重要的(后面在类里面会谈到)。 返回引用:const int &func2(); // 注意千万不要返回局部对象的引用,不然会报运行时错误:由于一旦函数结束,局部对象被释放,函数返回值指向了一个对程序来讲再也不有效的内存空间。 返回指针:const int *func3(); // 注意千万不要返回指向局部对象的指针,由于一旦函数结束,局部对象被释放,返回的指针变成了指向一个再也不存在的对象的悬垂指针。
class A { public: void func(); void func() const; const A operator+(const A &) const; private: int num1; mutable int num2; const size_t size; };
1.修饰成员变量
const size_t size; // 对于const的成员变量, [1]必须在构造函数里面进行初始化; [2]只能经过初始化成员列表来初始化; [3]试图在构造函数体内对const成员变量进行初始化会引发编译错误。
例如:
A::A(size_t sz):size(sz) // ok:使用初始化成员列表来初始化 { } A::A(size_t sz)
2.修饰类成员函数
void func() const; // const成员函数中不容许对数据成员进行修改,若是修改,编译器将报错。若是某成员函数不须要对数据成员进行修改,最好将其声明为const 成员函数,这将大大提升程序的健壮性。
const 为函数重载提供了一个参考
class A { public: void func(); // [1]:一个函数 void func() const; // [2]:上一个函数[1]的重载 …… }; A a(10); a.func(); // 调用函数[1] const A b(100); b.func(); // 调用函数[2]
如何在const成员函数中对成员变量进行修改???
下面提供几种方式(只提倡使用第一种,其余方式不建议使用)
(1)标准方式:mutable
class A { public: A::A(int i):m_data(i){} void SetValue(int i){ m_data = i; } private: mutable int m_data; // 这里处理 };
(2)强制转换:static_cast
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { static_cast<int>(m_data) = i; } // 这里处理 private: int m_data; };
(3)强制转换:const_cast
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { const_cast<A*>(this)->m_data = i; } // 这里处理 private: int m_data; };
(4)使用指针:int *
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { *m_data = i; } // 这里处理 private: int *m_data; };
(5)未定义的处理方式
class A { public: A::A(int i):m_data(i){} void SetValue(int i) { int *p = (int*)&m_data; *p = i } // 这里处理 private: int m_data; }; 注意:这里虽说能够修改,但结果是未定义的,避免使用!
3.修饰类对象
const A a; // 类对象a 只能调用const 成员函数,不然编译器报错。
4.修饰类成员函数的返回值
const A operator+(const A &) const; // 前一个const 用来修饰重载函数operator+的返回值,可防止返回值做为左值进行赋值操做。 例如: A a; A b; A c; a + b = c; // errro: 若是在没有const 修饰返回值的状况下,编译器不会报错。
1.要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
2.要避免最通常的赋值操做错误,如将const变量赋值,具体可见思考题;
3.在参数中使用const应该使用引用或指针,而不是通常的对象实例,缘由同上;
4.const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
5.不要轻易的将函数的返回值类型定为const;
6.除了重载操做符外通常不要将返回值类型定为对某个对象的const引用;
1.能够定义const常量,具备不可变性。
例如:
const int Max=100; int Array[Max];
2.便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
例如:
void f(const int i) { .........} 编译器就会知道i是一个常量,不容许修改;
3.能够避免意义模糊的数字出现,一样能够很方便地进行参数的调整和修改。
同宏定义同样,能够作到不变则已,一变都变!如(1)中,若是想修改Max的内容,只须要:
const int Max=you want;
便可!
4.能够保护被修饰的东西,防止意外的修改,加强程序的健壮性。
仍是上面的例子,若是在函数体内修改了i,编译器就会报错;
例如:
void f(const int i) { i=10;//error! }
5.为函数重载提供了一个参考。
class A { ...... void f(int i) {......} file://一个函数 void f(int i) const {......} file://上一个函数的重载 ...... };
6.能够节省空间,避免没必要要的内存分配。
例如: #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定义的常量在内存中有若干个拷贝。
7.提升了效率。
编译器一般不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操做,使得它的效率也很高。
{ size = sz; // error:试图在构造函数体内对const成员变量进行初始化 }