转载自: https://www.toutiao.com/i6644404128439075332/ 有删改。附参考 https://www.jb51.net/article/118141.htm
一 一般常量声明或定义的格式如下
const 类型说明符 变量名 常量或常量表达式 ; [1] 类型说明符 const 变量名 常量或常量表达式 ; [2][1]和[2]的定义是完全等价的。
例如
整形int 或其他内置类型 float,double,char
const int bufSize 512;
或者
int const bufSize 512;
因为常量在定义后就不能被修改 所以定义时必须初始化。
bufSize 128; // error:attempt to write to const objectconst string cntStr hello! ; // ok:initializedconst i, j 0; // error: i is uninitialized const
非const变量默认为extern。
const 对象默认为文件的局部变量。要使const变量能够在其他的文件中访问 必须显式地指定它为extern。
例如
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 对象。
例如
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 doubleconst 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 changeconst double *cdptr dVal; // ok;but can t change dVal through cdptr
不能使用指向const 对象的指针修改基础对象。然而如果该指针指向的是一个没const 对象 如cdptr 可用其他方法修改其所指向的对象。
那么如何将一个const 对象合法地赋给一个普通指针
例如:
const double dVal 3.14;double *ptr dVal; // errordouble *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 b c; // errro: 如果在没有const 修饰返回值的情况下 编译器不会报错。七 使用const的一些建议
1.要大胆的使用const 这将给你带来无尽的益处 但前提是你必须搞清楚原委 2.要避免最一般的赋值操作错误 如将const变量赋值 3.在参数中使用const应该使用引用或指针 而不是一般的对象实例 原因同上 4.const在成员函数中的三种用法 参数、返回值、函数 要很好的使用 5.不要轻易的将函数的返回值类型定为const 6.除了重载操作符外一般不要将返回值类型定为对某个对象的const引用八 cons有什么主要的作用
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常量分配存储空间 而是将它们保存在符号表中 这使得它成为一个编译期间的常量 没有了存储与读内存的操作 使得它的效率也很高。
本文链接: http://constab.immuno-online.com/view-770603.html