static与const
经典面试八股文第一题
static
1、修饰变量
初始化一次,存储在静态存储区,生命周期一直延续到程序结束的时候
修饰局部变量
初始化一次,存储在静态存储区,生命周期一直延续到程序结束的时候,但是不会改变它的作用域。
提问:作用域不变延长生命周期有什么意义呢?
比如局部变量所在函数被多次调用的时候,存储在静态区的局部变量只会初始化最开始的一次。
修饰全局变量
全局变量默认具有外部链接属性,可以被其他源文件访问
使用 static
修饰后,全局变量的链接属性变为内部链接,只能在声明它的源文件中访问,对其他源文件不可见,但是生命周期static修饰前后不发生变化。
static可以避免全局变量定义在头文件中可能导致的重复定义问题,不过c++17以上建议使用inline static修饰全局变量,其效果在修饰全局变量时等同于static。
2、修饰函数
与修饰全局变量类似,static
修饰的函数具有内部链接属性,不会与其他源文件中相同名称的函数产生冲突。因此,可以在不同的源文件中定义具有相同名称的 static
函数,而不会导致链接错误。但也因此,作用域被限制在当前定义它的源文件内部。
生命周期与程序运行周期相同。
3、修饰类成员
- 静态成员属于类,而不是类的实例。因此,它在内存中只有一份副本,被所有对象共享。
- 可以通过类名直接访问静态成员,也可以通过对象来访问。但建议使用类名来访问,以提高代码的清晰度和可读性。
- 静态⾮常量数据成员,其只能在类外定义和初始化,在类内仅是声明⽽已。
- 静态成员函数没有隐式的
this
指针。因为它不属于类的对象实例,所以在静态成员函数内部无法访问非静态成员变量和非静态成员函数。如果需要访问非静态成员,可以通过类的对象来访问,或者在静态成员函数内部创建一个对象。- this指针:在 C++ 中,
this
指针是一个隐含的指针,它指向当前对象。this
指针的初始化是在对象被创建时完成的。具体来说,当调用类的构造函数时,this
指针被初始化为指向正在创建的对象的地址。在构造函数的执行过程中,this
指针是有效的,并且可以在构造函数内部使用它来访问对象的成员。
注意:使用this
指针来访问对象的成员应该谨慎,确保在成员函数中的访问是安全的,并且不会导致未定义的行为。
this指针本质是非静态成员函数的一个隐含参数,所以他不占用对象本身的内存空间。
- this指针:在 C++ 中,
- 也正因为静态成员函数没有隐式的
this
指针,所以他不能被 virtual 修饰,虚函数的实现是为每⼀个对象分配⼀个 vptr 指针,⽽ vptr 是通过 this 指针调⽤的,所以不能为 virtual;虚函数的调⽤关系,this->vptr->ctable->virtual function。
const
一个关于存储区域的解释:
在C++中,全局变量、静态变量和常量通常存储在程序的数据段(data segment)中,这个数据段包含了全局存储区和静态存储区。因此,全局存储区和静态存储区实际上是指的同一个内存区域,用于存储在程序整个生命周期内都存在的数据。
实现机制:
- 编译器限制其修饰对象只读
- 内存管理:字面值常量放在数据段中,且只读;但是其他一般情况下,
const
变量的值通常存储在栈上或堆上 - 优化:编译器可能会利用
const
变量的不变性进行优化。例如,如果一个变量被声明为const
,并且在编译时其值是已知的,那么编译器可以将其用常量替换,从而避免在运行时进行计算。常量型变量->字面值变量
修饰
修饰变量,修饰函数参数,修饰返回值都是为了告诉编译器数据的只读属性;但是当const成员在对象构造的时候初始化的话,不同的对象其 const 数据成员值可以不同。
修饰成员函数(放在函数参数参数列表后面)
const 成员函数的主要⽬的是防⽌成员函数修改对象的内容。
注意,const 关键字和 static 关键字对于成员函数来说是不能同时使⽤的,静态成员函数不依赖于任何特定的对象实例,因此不涉及对象状态的修改,const 成员函数⼜必须具体到某⼀个函数。
修饰类对象
常量对象只能调⽤常量成员函数,别的成员函数都不能调⽤。一旦常量对象被初始化,就不能再修改它的值,因为常量对象被视为不可变。
当一个成员函数被声明为常量成员函数时(即在函数声明和定义中使用了 const
关键字),编译器会隐式地将该成员函数的第一个参数设为指向当前对象的常量指针 const MyClass* this,
当你尝试用一个常量对象调用非常量成员函数时,编译器会将 this
指针隐式地声明为 const MyClass* const this,这意味着在非常量成员函数内部,你不能通过
this
指针修改对象的状态,编译器会拒绝编译这样的调用。
但非常量对象可以调⽤类中的 const 成员函数,也可以调⽤⾮ const 成员函数。
探寻他们存储空间的代码:
#include <iostream> #define GLOBALSTRCONST "abc" using namespace std; int globalIndex{ 0 }; const int globalIndexConst{ 0 }; const string globalStrConst{ "abc" }; const char* p_globalStrConst = "abc"; const string *p_globalStrConstObj = new string{ "abc" }; const void Statictest() { int index{ 1 }; static int staticIndex{1}; for (; staticIndex < 10; staticIndex++) { staticIndex += staticIndex; cout << "staticIndex in Statictest :" << staticIndex << endl; } cout << "staticIndex adress in Statictest :" << &staticIndex << endl; cout << "index in Statictest :" << index++ << endl; cout << "index adress in Statictest :" << &index << endl; } class Test { public: static const int member = 1; static const int makeTest() { cout << "比较一下常量,全局变量,静态变量他们的位置吧。" << endl; return 0; } }; int main() { std::cout << "Hello World!\n"; { // static int index{}; static int staticIndex{0}; const int indexConst{ 0 }; const string strConst{ "abc" }; const char* const p_strConst = "abc"; const string* p_StrConstObj = new string{ "abc" }; cout << "Statictest() :" << index << endl; Statictest(); cout << "Statictest() :" << index << endl; Statictest(); cout << "index in main :" << index << endl; cout << "index adress in main :" << &index << endl << endl; cout << "------------------------------" << endl; Test::makeTest(); cout << "globalIndex adress:" << &globalIndex << endl; cout << "globalIndexConst adress:" << &globalIndexConst << endl; cout << "globalStrConst adress:" << &globalStrConst << endl; cout << "p_globalStrConst adress:" << &p_globalStrConst << endl; cout << "staticIndex adress:" << &staticIndex << endl; cout << "indexConst adress:" << &indexConst << endl; cout << "strConst adress:" << &strConst << endl; cout << "p_strConst adress:" << &p_strConst << endl; cout << "abc adress:" << &"abc" << endl; cout << "GLOBALSTRCONST adress:" << &GLOBALSTRCONST << endl; cout << "指针变量本身也是变量,&取址后看出他的地址并无特殊,但是字符串用不用new的时候,带不带*还是有区别的" << endl; cout << "p_strConst point value(?):" << p_strConst << endl; cout << "p_strConst value:" << *p_strConst << endl; cout << "p_globalStrConstObj adress:" << &p_globalStrConstObj << endl; cout << "p_globalStrConstObj value:" << *p_globalStrConstObj << endl; cout << "p_globalStrConstObj point value:" << p_globalStrConstObj << endl; cout << "p_StrConstObj point value:" << p_StrConstObj << endl; } }