简谈编程语言中的静态变量和函数
C++的静态变量/函数
C++中,静态变量生命周期贯穿程序,在程序开始运行时分配内存并初始化,程序结束运行时释放内存。
- 在继承结构中,如果子类未定义同名静态变量,父类和子类引用相同的静态变量,父类和子类静态变量地址一样;如果子类定义了同名的静态变量,那么父类和子类具有独立的静态变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream>
class Base { public: static int count; };
int Base::count = 0;
class Derived : public Base { };
int main() { Base::count = 5; std::cout << "Base::count: " << Base::count << std::endl; std::cout << "Derived::count: " << Derived::count << std::endl;
std::cout << "Base::count address: " << &Base::count << std::endl; std::cout << "Derived::count address: " << &Derived::count << std::endl; return 0; }
Base::count: 5 Derived::count: 5 Base::count address: 0x55a3f58cf154 Derived::count address: 0x55a3f58cf154
|
全局静态变量在.cpp文件级别声明,只在定义它的文件中可见,其他.cpp无法通过extern声名引用。除了静态变量,C++全局变量的生命周期也和程序相同,全局变量可以被其他.cpp文件引用,链接时会使相关.cpp文件引用相同的全局变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#include <iostream>
static int global_var = 10;
void display() { std::cout << "Global variable: " << global_var << std::endl; }
#include <iostream>
int main() { return 0; }
|
C++静态函数和上面的静态变量类似,
- 如果子类未定义同名静态函数,父类和子类引用相同的静态函数,父类和子类静态变量地址一样;如果子类定义了同名的静态函数,那么父类和子类具有独立的静态函数。
- 全局静态函数在.cpp文件级别声明,只在定义它的文件中可见,其他.cpp无法通过extern声名引用。C++全局函数的生命周期也和程序相同,全局函数可以被其他.cpp文件引用,链接时会使相关.cpp文件引用相同的全局变量。
静态变量/函数的符号未被导出,其他文件无法直接引用它。这是静态函数的设计初衷,用来实现模块化和封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream>
class Base { public: static void display() { std::cout << "Base static function" << std::endl; } };
class Derived : public Base { public: };
int main() { Base::display();
std::cout << "Base::display address: " <<reinterpret_cast<void*>(&Base::display) << std::endl;
Derived::display(); std::cout << "Derived::display address: " <<reinterpret_cast<void*>(&Derived::display) << std::endl; Derived d; d.display();
return 0; }
Base static function Base::display address: 0x55a1967b42bf Base static function Derived::display address: 0x55a1967b42bf Base static function
|
- C++静态函数不能配置成虚函数virtual, const和volatile
- static函数目的是编译时绑定,而virtual函数要求运行时绑定,二者不可兼容。虚函数表里也不会记录static函数
- const 类成员函数表示该函数不能被修改类成员变量,static函数不属于对象,本质通过类名调用,不能配置成const函数
- 静态变量可以被const, volatile修饰
JAVA的静态变量和函数
JAVA 静态变量和函数的逻辑和C++类似,不一样的使JAVA的类加载机制。JAVA除了静态变量和静态函数的概念,还有静态代码块的概念。类加载时,会初始化静态变量和执行静态代码块内容。
但不是每个类都会被加载,有两种加载类的条件
- 主动使用类时,如:创建类实例(new操作)。调用类的静态方法。访问类的静态字段。使用Class.forName()加载类。
- 子类初始化时,父类会被初始化。
C++不需要加载类,因为编译完C++代码就没有类的信息了,更不用说加载类。
通过子类引用父类的静态字段: 只会初始化父类,而不会初始化子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Parent { static { System.out.println("Parent initialized."); } static int value = 42; }
class Child extends Parent { static { System.out.println("Child initialized."); } }
public class Main { public static void main(String[] args) { System.out.println(Child.value); } }
Parent initialized. 42
|
常量在编译期已确定,不会触发类加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Example { static { System.out.println("Example initialized."); } static final int CONSTANT = 42; }
public class Main { public static void main(String[] args) { System.out.println(Example.CONSTANT); } }
42
|
JAVA类函数默认所有实例方法都是虚函数,支持多态,final、static、private方法除外。但静态函数不支持多态。
Golang 的全局函数/变量
Go语言中,没有像C/C++或Java中明确的“静态变量”概念。
全局变量在Go中可以用于实现类似静态变量的功能。全局变量, 作用域为整个包。如下函数中,它的值在每次调用 increment 函数后都会保留。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
var counter int
func increment() int { counter++ return counter }
func main() { fmt.Println(increment()) fmt.Println(increment()) }
1 2
|
Go的标准库提供了 sync.Once,可以确保某段代码只执行一次(类似静态初始化)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import ( "fmt" "sync" )
var once sync.Once var config string
func initConfig() { once.Do(func() { fmt.Println("Initializing config...") config = "LoadedConfig" }) }
func main() { initConfig() fmt.Println(config)
initConfig() }
Initializing config... LoadedConfig
|
组合对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package main
import "fmt"
type Person struct { Name string Age int }
type Employee struct { Person Position string }
func main() { per := Person{Name: "Alice", Age: 20} emp := Employee{ Person: per, Position: "Developer", } fmt.Println(emp.Name) fmt.Println(emp.Age) fmt.Println(emp.Position) }
Alice 20 Developer
|
Python的类函数和静态函数
Python 没有对变量的静态修饰,只有对函数的classmethod和staticmethod修饰
类函数的第一个参数是 cls,表示调用该方法的类本身,而不是实例。类函数可以访问类的属性和方法,不能直接操作实例属性。类函数通过类名调用
Python在类内部直接定义的变量是类变量,比如下面的static_var,self定义的变量是实例变量。前者可以通过cls访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MyClass: static_var = 0
def __init__(self): MyClass.static_var += 1
@classmethod def get_static_var(cls): return cls.static_var
a = MyClass() b = MyClass() print(MyClass.static_var) print(a.get_static_var())
|
静态方法不需要传递 self 或 cls 参数。不能访问类或实例的属性,只能执行独立的功能。同样通过类名调用。
1 2 3 4 5 6 7
| class MyClass: @staticmethod def static_method(): print("This is a static method.")
MyClass.static_method()
|