const
对于const关键字,我们习惯性的将它直接看作常量,但当你编译下面代码时,会发现与我们之前的理解有所偏差:
const int SIZE;
int arr[SIZE];
//.c 不能通过
//.cpp 顺利运行
在定义数组时,必须指定数组元素个数为常量,但在C中却不能通过,以此可以推翻const修饰的值是常量。参考《C语言深度剖析》,可以将const修饰的值看作readonly属性,而C++之所以可以通过,是因为在C++中拓展了对const的含义。
下面针对const在不同编译环境下做深入理解:
- C
修饰一般变量 —只读变量
修饰数组—只读数组
//定义同时必须初始化
//const 与 类型名可交换顺序; const修饰的变量存放在 .rodata段,只读数据段
const int i = 10;
int const arrar[3] = {1, 2, 3};
修饰函数参数—不可改变
修饰函数返回值—不可改变
void func(const char* s);
const char* func();
修饰指针—编译器解析时忽略类型名,可以看作除类型名之外const修饰离其最近的
//下面我针对不同情况举例分析:
const int *p; //const修饰*p,即p指向内容不可变
int const *p; //const修饰*p
int *const p; //const修饰p,即p本身不可变
const int *const p;//前一个const修饰*p, 后一个const修饰p
注:
对于C中const修饰的值并不是真正安全的(即真正只读/不可改变),通过下面举例分析便可以得到原因:
const int i = 10;
int *p = i;
//在C编译其中默认支持用const int*类型去初始化int *类型
//诸如此类,那么我们必然会考虑到通过指针p可以去改变i的值
*p += 10;
//此时,i的值便被改变了
1.只读:更安全的宏替换
2.不可改变:实质上是指不可直接去改变。
- C++
对于C++来说,相比C对const扩展了不同含义。
扩展1、
常量:定义同时初始为立即数。
常变量:初始值为变量,则在编译时期不确定。
int a = 10;
const int N = 10;//N为常量
const int M = a; //M为常变量
//预编译阶段直接进行简单宏替换,
int b = N; // int b = 5;
int c = M; // int c = a;(在编译时期不确定)
扩展二、
任何试图将一个非const对象的指针指向const对象的动作,都将引起编译错误。
const int *a = 10;
const int *p = a;
int const *q = a;
//此时p和q通过const修饰限定其指向内容不可改变,即保证了a的安全性。
//但p和q自身指向可以改变,不过也对a毫无影响了。
使用const的必要性:
1.更安全的宏替换,减少不必要的内存分配、存储与读取。
2.代码可读性与可维护性。
将特殊值或对象通过全局只读变量代替,易于辨识和后期修改。
static
修饰一般变量
首先,对于static修饰的变量,均有以下特点:
- 默认值为0
- 按程序的生命周期来分配和释放变量
- 同所有未初始化的全局变量一样均放在 .bss段
- 初始化:
编译阶段—静态初始化放置在.bss段默认为0;
运行阶段—动态初始化仅在首次调用处进行内存分配并赋值。
1、 全局静态变量:在全局变量前加上static,使该变量仅在当前文件可用。
与普通全局变量区别:限制了所修饰变量的作用域。
2、局部静态变量:在局部变量前加上static,使该变量在当前函数体内恒有效,即仅首次调用该函数时对此变量进行初始化,并且直至程序运行结束才释放此变量。
与普通局部变量区别:拓展了所修饰变量的生命周期。
//下面通过代码实际测试一下:
static int a;
static int b = 0;
//这种用户直接将b初始化为0的操作与编译器对a的处理是一样的,因此也会将b放置在.bss段
void func()
{
static int i = 1;
int j = 1;
i++;
j++;
cout<<i<<j<<endl;
}
int main()
{
cout<<a<<endl;//打印a=0
func();//打印i = 2; j = 1
func();//打印i = 3; j = 1
}
修饰数组
- 数组内元素会默认初始化为0
修饰指针 类似修饰一般变量,可用于回调函数
修饰函数
- 函数只能在当前文件中被调用,即限定函数作用域
- 函数调用结果不会修改任何非静态成员。
在C++中对static的扩充:
1、修饰对象:该对象在构造同时其成员相应内存都被初始化为0。
类内/类模板中static修饰的成员变量/对象,必须在类外进行初始化,其原因就是在编译阶段static所修饰的变量仅保存在.bss段中,而且对于类模板来说,只有在调用时才会对相应成员进行实例化一份,因此在运行期间可能会出现变量名相同产生错误,而对其初始化就是起到占位作用。
2、修饰成员函数:静态成员函数没有this指针,只能访问静态数据成员。
默认静态成员函数
3、修饰成员变量:限定该成员有且只能有一个实例化对象,即对每个类类型仅有一份拷贝,由该类类型的所有对象共享访问。
适用于单例模式:
//下面就实现一个简单的饿汉模式示例:
class signle
{
public:
static single *getInstance()
{
if(pInstance == NULL)
{
if(pInstance == NULL)
pInstance = new signle();
}//双重判断保证多线程安全
return pInstance ;
}
private:
single (){}
static single *pInstance ;
};
single *single::pInstance = NULL;
//此时通过对象指针查看对象地址会发现均指向同一块内存空间