
static 关键字
1. static 关键字
static
的用法有三种,分别是:
静态局部变量
静态全局变量
静态函数
1.1 静态局部变量
static修饰局部变量时,会影响局部变量的生命周期,本质上改变了局部变量的存储位置,将局部变量从栈区放到了进程中的静态区(全局数据区),生命周期变为整个周期,直到程序运行结束。
用于函数体的内部修饰变量,这种变量的生存期长于所在函数。
静态局部变量在程序执行到该对象的声明处时会被首次初始化
,即以后的函数调用不在进行初始化(局部变量每次函数调用都会被初始化)
对于一个完整的程序,在内存中分布情况如下:
栈区(stack):由编译器自动分配释放,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。
堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收。分配方式类似于链表。
全局区(静态区)(static):存放全局变量、静态数据、常量。程序结束后由系统释放。
文字常量区:常量字符串就是存放在这里的。程序结束后由系统释放。
代码区:存放函数体(类成员函数和全局函数)的二进制代码。
1.2 静态全局变量
全局变量的属性:全局变量具有外部链接属性。而
static
修饰全局变量时,这个全局变量的外部链接属性变为内部链接属性,是其他源文件(.c)文件就可以再使用这个全局变量了。则使用时我们会感觉到该全局变量作用域变小。
改变该函数的链接属性,让该函数具有文件作用域,即只能在当前文件中使用静态函数。其它文件中可以定义相同名字的函数,不会发生冲突
1.3 静态函数
类似于static修饰全局变量,函数同样具有外部属性。而static修饰函数时,这个函数的外部链接属性变为内部链接属性,是其他源文件(.c)文件就可以再使用这个函数了。则使用时我们会感觉到该函数作用域变小。
1.4 静态成员
静态成员分为:静态数据成员,静态成员函数。(在成员变量和成员函数前加上关键字static
)
(1)静态数据成员
静态数据成员(也称为静态成员变量)是类级别的变量,与类的任何对象实例无关。静态数据成员是被所有的类对象共享的,这意味着无论创建了多少了对象,静态数据成员就只有一份,只要有一个对象修改了静态数据成员,其他对象的该成员也会改变。
静态数据成员的特点:
共享性:类的所有实例共享同一个静态变量。
初始化:关键字static只出现类的内部。在类外定义且初始化,初始化的格式如下:数据类型 类名::静态成员变量名 = 初值;
访问方式:类名::直接访问,也可以通过类的实例访问,或者成员函数直接访问。但是,通常推荐通过类名::(类作用符)来访问静态成员,因为这更符合其“属于类而非特定对象”的语义。
存储位置:静态成员变量存储在程序的静态存储区域中,这意味着它们的生命周期贯穿整个程序执行期间。静态成员变量在对象创建之前就已经被分配了内存空间。
示例:
#include <iostream>
using namespace std;
class MyClass {
public:
static int count; // 声明静态成员变量
MyClass() {
count++; // 在构造函数中更新静态成员变量的值
}
static void displayCount() {
cout << "Object count: " << count << endl;
}
};
// 初始化静态成员变量
int MyClass::count = 0;
int main() {
MyClass obj1;
MyClass obj2;
MyClass obj3;
MyClass::displayCount(); // 输出:Object count: 3
return 0;
}
在这个例子中,MyClass 类有一个静态成员变量 count,用于跟踪创建的 MyClass 实例的数量。每当创建一个 MyClass 的新实例时,其构造函数都会递增 count 的值。静态成员函数 displayCount 用于显示当前的 count 值。其中,静态成员变量 count 是在类外部进行初始化的。
✅总结
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
(2)静态成员函数
static修饰类的成员函数,由一个类的所有对象共享。
使用目的:访问同一类中的静态成员函数,维护对象之间共享的数据。 静态成员函数一般不能访问非静态成员 (包括成员函数和数据成员),静态成员函数只能访问静态成员变量。但是非静态可以访问静态。 因为静态函数属于类而不是整个对象,静态成员函数没有隐含的this指针。所以他无法访问自己类的非静态成员。
静态成员函数的调用方式:
在类外调用,两种方式:
类名::静态公有成员函数名(实参表)
对象名.静态公有成员函数名(实参表)
在类内调用:
直接调用静态成员函数
1.5 非静态成员
非静态数据成员
类的每个实例都有一份拷贝(动态区)。
访问方式:类的实例化对象,类指针。
非静态成员函数(普通成员函数)
非静态成员函数属于类的实例化对象所有,所以只有在创建类的实例化对象的时候才会分配内存,访问类的非静态函数要通过对象来实现。
访问方式:非静态成员函数通过对象(或对象指针)来访问,有this指针。
✅总结
静态数据成员:是类的一部分,为类的所有实例共享(静态区)
非静态数据成员:类的每个实例都有一份拷贝(动态区)
静态数据成员先于类的实例化对象而存在,没有有this指针;非静态数据成员存在于类的实例化对象之后,具有this指针。
静态成员函数为什么只能访问静态成员变量
1.6 常见问题
问题1:静态成员函数为什么只能访问静态成员变量?
在 C++ 中,静态成员函数(static
)只能访问静态成员变量,原因主要涉及 对象作用域、存储方式和 this
指针
静态成员函数在类的作用域内存在,但它:
不属于某个具体对象,而是属于整个类。
没有
this
指针,无法访问非静态成员。可以在没有创建对象的情况下直接调用(
ClassName::StaticFunction()
)。
class Example {
private:
int nonStaticVar = 10; // 非静态成员变量
static int staticVar; // 静态成员变量
public:
static void staticFunction() {
// std::cout << nonStaticVar; ❌ 错误:无法访问非静态变量
std::cout << staticVar << std::endl; // ✅ 只能访问静态成员
}
};
// 静态成员变量初始化
int Example::staticVar = 100;
int main() {
Example::staticFunction(); // 直接调用静态函数
return 0;
}
问题2:static和const的区别?
在C++中,static
和const
都是用于修改变量或函数属性的关键字,但它们具有不同的用途和效果。
🚀static
(1)在变量上的作用
局部静态变量:在函数内部定义的静态变量。它的生命周期是整个程序执行期间,而不是只在定义它的函数执行期间。这意味着,即使函数执行完毕,局部静态变量的值仍然保留,下次函数调用时该值仍然存在。
void func() {
static int count = 0;
count++;
std::cout << count << std::endl;
}
每次调用func()
,count
的值都会增加。
全局静态变量:在文件作用域内定义的静态变量。它只对该文件可见,其他文件无法直接访问它。这有助于隐藏实现细节,实现信息封装。
静态类成员:在类内定义的静态成员变量。它属于类本身,而不是类的实例。所有类的实例共享同一个静态成员变量( this)
(2)在函数上的作用
静态函数:在类内定义的静态成员函数。它只能访问类的静态成员变量和其他静态成员函数,不能访问非静态成员。静态成员函数也不依赖于类的特定实例
🚀const
const
关键字用于声明常量,即其值在初始化后不能再被修改。
(1)常量变量
使用const
声明的变量是常量,其值在初始化后不能被修改。
const int x = 10; // x的值不能被修改
(2)常量指针和指针常量
const
可以用于修饰指针,可以声明指向常量的指针(指针常量)或常量指针(指向常量的指针)
技巧:看离谁最近,离谁近就修饰谁。
const int* p1 = &x; // 指向常量的指针,p1可以指向其他变量,但不能通过p1修改x的值
int const* p2 = &x; // 同上
int* const p3 = &y; // 指针常量,p3的值(即它所指向的地址)不能被修改,但可以通过p3修改y的值
(3)常量成员函数
在类内定义的常量成员函数。它不能修改类的任何数据成员(除非它们是mutable
的)。这有助于确保函数不会意外地修改对象的状态。
区别:
生命周期和可见性:
static
主要关注变量的生命周期和可见性,而const
主要关注变量的值是否可变。用途:
static
用于控制变量的存储和链接属性,以及隐藏实现细节;而const
用于定义常量,确保变量的值在程序执行期间不被修改。与函数的关系:
static
可以应用于函数,使其只在定义它的文件内可见;而const
可以应用于成员函数,确保它们不会修改对象的状态。