Skip to content

静态成员

有时在进行对象的操作时,我们可能会希望无论怎样修改,同一个类的不同对象间共享同样的数据,而共享的数据我们就叫做静态成员

类的成员分为两种:

  • 实例成员:每个对象都有一份,各个对象间数据独立,必须创建该类的实例才能够访问。

  • 静态成员:直接属于类本身,所有对象共享一份,可以通过类名访问(虽然也能通过实例访问,但不推荐)

因此,@export@onready注解并不能用在静态变量上,也无法将局部变量或匿名函数声明为静态

父类的静态成员也能够在子类中访问

静态成员包括静态变量和静态方法,使用static关键字定义静态成员

gdscript
static var a

static func foo():
    pass

静态变量也可以指定类型,设置getter和setter函数,但不能在访问器中访问实例成员,调用实例方法

gdscript
static var a: int:
    get:
        return a
    set(value):
        a = value

我们知道,self表示的是当前类的实例,而静态方法直接属于类,无法访问当前类的实例成员(不依赖具体对象)

因此静态方法中除了不能使用类的实例成员外,也不能使用self(没有“当前实例”的概念)

当然,这并不代表静态方法完全不能访问实例成员,静态方法自己不能访问类的实例成员(因为没有 self ),但如果你通过参数传进来一个对象,就可以照样访问这个对象的实例成员,无论这个对象是不是当前类的实例。

因此,静态方法适合用于编写工具方法,不需要创建类的实例直接使用类名就能使用它们

gdscript
static func add(a: int, b: int) -> int:
    return a + b
    
static func is_name_starts_with(node: Node, prefix: String) -> bool:
    return node.name.begins_with(prefix)

静态属性在所有类的实例中共享状态,因为它是属于类而非实例的

gdscript
# student.gd
class_name Student

static var class_size: int = 0

var name: String
var id: int

func _init(name: String) -> void:
    self.name = name
    class_size += 1
    id = class_size
    
# main.gd
extends Node

func _ready():
    var student1 := Student.new("张三")
    var student2 := Student.new("李四")
    
    print(student1.id) # 1
    print(student2.id) # 2
    
    print(Student.class_size) # 2
    
    # 尽管可以这样访问,但不推荐
    print(student1.class_size) # 2
    print(student2.class_size) # 2

练习

  1. 创建一个名为 Counter 的类,包含一个静态变量 count,用于统计创建了多少个 Counter 实例。

    提示:每当调用 Counter.new() 创建一个实例时,记得在 _init() 中更新 count

  2. 设计一个名为 MathTool 的类,添加两个静态方法:

    1. add(a: int, b: int):返回两个整数的和

    2. is_even(n: int):返回这个数是不是偶数

  3. 创建一个 Student 类,定义:

    • 静态变量 next_id,用于给每个学生分配一个唯一的 id

    • 每个学生实例有 nameid 字段

    • 构造函数需求一个参数,学生的名字

    • 每次创建新学生时初始化学生的名字,并自动递增 next_id 赋给该学生

  4. 想一想:你是否遇到过在项目中使用静态方法或静态变量的场景?写一个简单的例子或思路,用静态成员去「记录」或「工具化」你的逻辑,比如记录游戏中总共击败了多少敌人