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++中,staticconst都是用于修改变量或函数属性的关键字,但它们具有不同的用途和效果。

🚀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的)。这有助于确保函数不会意外地修改对象的状态。

区别

  1. 生命周期和可见性static主要关注变量的生命周期和可见性,而const主要关注变量的值是否可变。

  2. 用途static用于控制变量的存储和链接属性,以及隐藏实现细节;而const用于定义常量,确保变量的值在程序执行期间不被修改。

  3. 与函数的关系:static可以应用于函数,使其只在定义它的文件内可见;而const可以应用于成员函数,确保它们不会修改对象的状态。