const 与 static
const 与 static 关键字
const
- const修饰符用来定义常量,具有不可变性。在类中,被const修饰的成员函数,不能修改类中的数据成员;
- 指针常量指的是该指针本身是一个常量,不能被修改,但是指针指向的对象可以被修改;
- 常量指针指的是这个指针指向的对象是一个常量,不能被修改,但是指针本身可以被修改。
- 如果 const 变量是在全局作用域中声明的,它将存储在静态存储区(Static Storage Area)中。
- 如果 const 变量是在函数内部或代码块内部声明的,它将存储在栈(Stack)上,在函数返回时释放。
- const 修饰的字符串常量存储在常量存储区,在程序运行期间保持不变。
const 修饰的函数能否重载?
- const修饰的函数可以重载。const成员函数既不能改变类内的数据成员,也无法调用非const的成员函数;const类对象只能调用const成员函数。非const对象无论是否是const成员函数都能调用,但是如果有重载的非const函数,非const对象会优先调用重载后的非const函数。
const 修饰函数的参数
- 如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const修饰,否则该参数将失去输出功能。
- 如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针,起到保护作用。
1 | void StringCopy(char*strDestination, const char *strSource); |
- 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将
void Func(A a)
改为void Func(const A &a)
。 - 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如
void Func(int x)
不应该改为void Func(const int &x)
。
const 修饰函数的返回值
- 如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。例如如下函数:
1 | const char* GetString(void); |
- 如下语句将出现编译错误:
1 | char* str = GetString(); |
- 正确的用法时:
1 | const char* str = GetString(); |
- 如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。 例如:
不要将函数int GetInt(void)
写成const int GetInt(void)
。 - 同理不要把函数
A GetA(void)
写成const A GetA(void)
,其中A为用户自定义的数据类型。 - 如果返回值不是内部数据类型,将函数
A GetA(void)
改写为const A& GetA(void)
的确能提高效率。 - 但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
- 函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
1 | class A |
- 如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。上例中,语句
a = b = c
仍然正确,但是语句(a = b) = c
则是非法的。
const 成员函数
- 任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误。
- 这无疑会提高程序的健壮性。以下程序中,类stack的成员函数GetCount仅用于计数,从逻辑上讲GetCount应当为const函数。编译器将指出GetCount函数中的错误。
补充:const放在后面是什么意思?
1 | AcGePoint3dstartPoint() const; |
- const放在后面跟前面有区别么?准确的说const是修饰this指向的对象的
- 譬如,我们定义了
1 | class A |
- 这里
f
函数其实有两个参数,第一个是A* const this
, 另一个才是int类型的参数 - 如果我们不想
f
函数改变参数的值,可以把函数原型改为f(const int)
,但如果我们不允许f
改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是const A* const this
。 - const修饰
*this
是本质,至于说“表示该成员函数不会修改类的数据。否则会编译报错”之类的说法只是一个现象,根源就是因为*this
是const类型的。
1 | class Stack |
static
静态数据的存储
- static 变量在类的声明中不占用内存(未赋值),因此必须在.cpp文件中定义类静态变量以分配内存;
- 文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化(已赋值);
- 局部静态变量在第一次使用时分配内存并初始化;
- 赋予字面值时会在编译阶段就被初始化, 加载时将其映射到内存空间。
- 全局(静态)存储区:分为
DATA
段和BSS
段。DATA
段(全局初始化区)存放初始化的全局变量和静态变量;BSS
段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。 - 其中
BBS
段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。 - 存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
- 在 C++ 中 static 的内部实现机制:静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
static 修饰全局变量
- static 修饰全局变量可以将变量的作用域限定在当前文件中,使得其他文件无法访问该变量。
- static 修饰的全局变量在程序启动时被初始化(可以简单理解为在执行 main 函数之前,会执行一个全局的初始化函数,在那里会执行全局变量的初始化),生命周期和程序一样长。
static 修饰局部变量
- static 修饰局部变量可以使得变量在函数调用结束后不会被销毁,而是一直存在于内存中,下次调用该函数时可以继续使用。
- 由于 static 修饰的局部变量的作用域仅限于函数内部,所以其他函数无法访问该变量。
static 修饰函数
- static 修饰函数可以将函数的作用域限定在当前文件中,使得其他文件无法访问该函数。
- 由于 static 修饰的函数只能在当前文件中被调用,因此可以避免命名冲突和代码重复定义。
static 修饰类成员变量和函数
- static 修饰类成员变量和函数可以使得它们在所有类对象中共享,且不需要创建对象就可以直接访问。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SleepyLoser's Blog!
评论