const 用法
const 修饰局部变量
// 等效的写法
const int n = 1; // n 必须初始化
int const n = 1; // n 必须初始化
const 修饰常量静态字符串
const char* str="fdsafdsa";
如果没有const的修饰,我们可能会在后面有意无意的写str[4]=’x’这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。
const 修饰指针
int n;
int * const p = &n; // 指针常量,指针只能在初始化时指向某一对象,但可以通过指针修改所指对象的值
const int * p = &n; // 常量指针,指针可以指向任意对象,但不能通过指针修改所指对象的值
int const * p = &n; // 常量指针,等效
const 修饰类对象/引用/指针
class TEMP {
void func1();
void func2() const;
};
const TEMP temp;
temp.func1(); //错误;
temp.func2(); //正确;
- 不能改变对象的任何成员变量
- 不能调用任何非const成员函数,因为任何非const成员函数都会有修改成员变量的可能
const 修饰类数据成员
- 不能被修改
- 只能在类初始化列表中被赋值
总结:只能在初始化列表中初始化的情况
- const成员变量
- 引用变量
- 基类构造函数
const 修饰类成员函数
void func (void) const;
- 不能修改类的任何非静态数据成员(可以修改静态数据成员)
- 不能调用类的任何非const函数(可以调用const成员函数)
- 对于const类对象,只能调用类中的const成员函数,所以const修饰成员函数的作用主要就是限制对const对象的使用。
const常量与define宏定义的区别
编译器处理方式不同
define宏是在预编译阶段展开
const常量是编译运行阶段使用
类型和安全检查不同
- define宏没有类型,不做任何类型检查,仅仅是展开
- const常量有具体的类型,在编译阶段会执行类型检查,可以进行调试
- 存储方式不同
define宏不占内存,宏只是在预处理时展开,但展开后占内存,每次替换都是一次内存拷贝
const定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个拷贝。
总结
- const节省了空间,避免了不必要的内存分配,同时提高了效率。编译器通常不为普通的const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。C++中是不太推荐用宏的,尽量少用.因为C++是强类型的语言,希望通过类型检查来降低程序中的很多错误,而宏只是在编译期前做简单替换,绕过了类型检查,失去了强类型系统的优势支撑。
mutable关键字
class ST {
public:
int a;
mutable int showCount;
void Show()const;
};
void ST::Show()const {
//a=1;//错误,不能在const成员函数中修改普通变量
showCount++;//正确
}
在C++中,mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其mutable成员也可以被修改。mutable只能修饰非静态数据成员。
const_cast
const_cast<type_id> (expression)
- 该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的
- 常量指针被转化成非常量的指针,并且仍然指向原来的对象
- 常量引用被转换成非常量的引用,并且仍然指向原来的对象
- const_cast一般用于修改底指针。如const char *p形式
static 变量
//main.c文件中
int a = 0; //全局变量
static int b=0;//静态全局变量
main()
{
int c; //局部变量
static int d;//静态局部变量
}
-
全局变量
具有全局作用域,全局变量只需在一个源文件中定义,其他文件就可以在使用extern外部声明后直接使用。未初始化时,默认为0。
-
静态全局变量
具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被 static 关键字修饰过的全局变量具有文件作用域。为初始化时,默认为0。
-
局部变量
具有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。未初始化时,默认值未定义。
-
静态局部变量
具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在。它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它。未初始化时,默认值为0。
一个静态局部变量的例子
#include<stdio.h> static int j; //修饰静态的全局变量 int funOne(void) { static int i = 0;//修饰静态的局部变量 i++; return i; } int funTwo(void) { j = 0; j=j+2; return j; } int main() { int k = 0,m = 0,n = 0; for(k = 0;k < 5;k++) { m = funOne(); n = funTwo(); printf("m=%d,n=%d\n",m,n); } return 0; }
结果显示如下
m=1,n=2
m=2,n=2
m=3,n=2
m=4,n=2
m=5,n=2
static 函数
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
- 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
- 不同的文件可以使用相同名字的静态函数,互不影响
c++面向对象中static的使用特性
静态数据成员
在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:
- 静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义
- 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作
- 和普通数据成员一样,静态数据成员也遵从public, protected, private访问规则
- 静态数据成员的初始化格式:<数据类型><类名>::<静态数据成员名>=<值>
- 类的静态数据成员有两种访问方式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>
同全局变量相比,使用静态数据成员有两个优势:
- 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性
- 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能
静态成员函数
与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:
- 静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数或访问其余静态数据成员
- 出现在类体外的函数定义不能指定关键字static
- 非静态成员函数可以任意地访问静态成员函数和静态数据成员
总结
static是一个很有用的关键字,使用得当可以使程序锦上添花。当然,有的公司编码规范明确规定只用于本文件的函数要全部使用static关键字声明,这是一个良好的编码风格。
无论如何,要在实际编码时注意自己的编码习惯,尽量体现出语言本身的优雅和编码者的编码素质。
参考
https://www.jb51.net/article/118141.htm
https://www.cnblogs.com/qiaoconglovelife/p/5322521.html
https://www.bbsmax.com/A/pRdB8G06Jn/
https://blog.csdn.net/guotianqing/article/details/79828100
https://blog.csdn.net/weixin_42167759/article/details/80287748
May 24, 2020 PM 23:17 @DaYang