static关键字修饰成员变量与成员函数

简介: 1. static概念声明 为static的类成员称为类的静态成员,用static修饰的成员变量,称为静态成员变量;用static修饰的成员函数,称为静态成员函数,都存放在堆区。静态成员变量一定要在类外进行初始化。

1. static概念

声明static的类成员称为类的静态成员,用static修饰的成员变量,称为静态成员变量;用static修饰的成员函数,称为静态成员函数,都存放在堆区。

静态成员变量一定要在类外进行初始化

为什么静态成员变量一定要在类外进行初始化呢?


在类中,仅仅是声明了静态变量(告诉我们有这个成员变量),并没有定义(定义需要分配内存)。


声明只是表明了变量的数据类型和属性,并不分配内存;定义则需要分配内存。 注意:如果在类里面这么写 int a ;那么既声明了变量,也定义了变量,两者合在一起了。


静态成员是“类级别的”,它和类的地位等同,而普通成员是“对象(实例)级别的”。类级别的成员,先于该类的任何对象而存在,是属于所有对象的,被该类的所有对象共享。


现在,咱们假定要实例化该类的一个对象,那么会发生什么事情呢?


静态成员肯定要出现在这个对象里面的,对吧?这时候才去定义那个静态成员吗?显然不合适!因为,比如有另外一个线程也要创建该类的对象,那么也要按照这个方式去定义那个静态成员。

这可能会产生两种情况:

A. 重复定义;

B. 就算不产生重复定义的情况,也会产生竞争,造成死锁。

显然编译器不能这么干。很合理的解决办法,就是在类的外部事先把它定义好,然后再供所有的对象共享。

class A
{
public:
  static int _a;
  static int _b;
};
int A::_a = 10;   // 定义静态成员变量,同时也初始化。
int A::_b;   // 定义静态成员变量,不初始化也可以。
int main()
{
  A T1;
  cout << T1._a << endl;  // 10
  cout << A:: _b << endl;  // 0
  return 0;
}

静态成员函数既可以在类内定义,也可以在类外定义,类外定义不需要加static。

普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。

编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。


普通成员变量占用对象的内存,静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,也就是说静态成员函数不能访问普通成员变量,只能访问静态成员变量。


普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。

class A
{
public:
  A() { ++_count; }
  A(const A& t) { ++_count; }
  ~A() { --_count; }
  static int GetACount()    // 类内定义,只能访问静态成员
  {
    return _count; 
  }
private:
  static int _count;
};
int A::_count = 0;
void TestA()
{
  cout << A::GetACount() << endl;   // 可以直接调用类
  A a1, a2;
  A a3(a1);
  cout << a3.GetACount() << endl;   // 也可以直接使用对象调用
}
int main()
{
  TestA();
  return 0;
}

总结:

静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。

2. 使用细节

特性:

  1. 静态成员为所有类对象共享,存放在静态区(在对象实例化之前就已经定义了)。
  2. 静态成员变量必须在类外定义,定义时不添加static关键字。
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制。

3. 易错点

问题1:

静态成员函数可以调用非静态成员函数吗?

不可以,因为静态成员函数没有this指针,普通成员函数必须有相应的对象地址(this)才能调用,因为普通函数里面要用this来访问对象。


问题2:

非静态成员函数可以调用类的静态成员函数吗?

可以,静态成员函数属于整个类,既可以使用类域(A::)进行调用,也可以使用对象(a1.)进行调用。

目录
相关文章
|
Java
clone()方法使用时遇到的问题解决方法(JAVA)
我们平时在自定义类型中使用这个方法时会遇到的 4 个问题。
307 1
|
Arthas Kubernetes 数据可视化
推荐10个GitHub上适合练手的后端项目(涵盖初中高阶)
上周,我们推出了26个好玩又有挑战的前端练习项目。 不少同学留言说,那后端的呢?后端也要! 淘系工程师一呼就应,我们邀请了2位淘系技术后端工程师,筛选出10个难度层层递进,好玩且实用的后端项目,包含java类库中的“瑞士军刀”工具、可视化API展现等等,难度依然分为【初级篇:4个】、【中级篇:3个】、【高级篇:3个】,不同学习诉求的同学可按需选择~
推荐10个GitHub上适合练手的后端项目(涵盖初中高阶)
|
IDE PyTorch 网络安全
|
7月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
456 4
AI代理内存消耗过大?9种优化策略对比分析
|
9月前
|
人工智能 开发框架 前端开发
斩获3K+ star,再见传统开发!这款开源AI后台开发框架让效率提升300%
ruoyi-ai 是基于 ruoyi-plus 框架开发的开源 AI 平台,集成 ChatGPT4、DALL·E-3 和 MidJourney 等前沿模型,提供聊天、绘画、语音克隆等全栈式 AI 能力。其核心价值在于多模态交互与企业级部署支持,开发者可快速搭建智能应用,个人用户亦能轻松体验 AI 创作魅力。项目支持自定义知识库训练、AI 绘画生成、语音克隆、弹幕互动等功能,采用 Java17+SpringBoot3.X 技术栈,前后端分离设计,具备高效性能与扩展性。相比同类项目,ruoyi-ai 提供更丰富的功能组合和企业级管理能力,适用于多种场景需求。
1292 3
|
11月前
|
Java
java构造方法,构造代码块,静态代码块的执行顺序
本文介绍了Java中构造方法、构造代码块和静态代码块的执行顺序。静态代码块用`static`声明,在JVM加载类时执行一次;构造代码块在每次创建对象时执行,先于构造方法;构造方法用于对象初始化,创建对象时调用。示例代码展示了这三者的输出顺序,并解释了它们的区别和应用场景。
358 1
|
10月前
|
传感器 人工智能 机器人
杭州六小龙最新开源「空间理解模型」,保姆级教程来了!
前几天,“杭州六小龙”之一「群核科技」在GTC 2025大会开源了空间理解模型:SpatialLM。
569 3
|
Web App开发 搜索推荐 开发者
浏览器插件上架指南:如何把你的产品搬上浏览器插件市场
在实践了 Chrone、Firefox、Edge、Opera 等 几个主要的插件平台的上架发布工作后,我觉得很有必要把这个过程和思考记录下来,分享给大家,希望能提供一些参考和避坑的经验。我想通过这篇文章,和大家聊聊「为什么我要做这件事」,以及「这个系列文章会包含哪些内容」。我想用一个系列的文章,记录我是如何把 EmojiClick 搬到浏览器插件市场的,也给大家提供一些借鉴经验。
351 19
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
987 4
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
存储 缓存 关系型数据库
【MySQL调优】如何进行MySQL调优?一篇文章就够了!
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
【MySQL调优】如何进行MySQL调优?一篇文章就够了!