类外
静态变量或函数意味着,当需要将这些变量或函数与实际定义的符号链接时,链接器不会在这个翻译单元的作用域之外寻找那个符号定义,即只会在这个翻译单元内部链接(文件内可用)
如果这句话并不理解,可以看一下【C++】How the C++ Compiler Works和【C++】How the C++ Linker Works
示例:
Main.cpp
#include<iostream>
int s_Variable = 10;
int main() {
std::cout << s_Variable << std::endl;
}
Static.cpp
static int s_Variable = 5;
没有问题,输出10
当删掉static int s_Variable = 5;
中的static后,报错了
我们可以将int s_Variable = 10;
改为extern int s_Variable;
没有问题,输出5
函数也是类似的,动手试一试吧
类内
静态成员:指的是在C++类中声明成员时,可以加上static关键字,这样声明的成员叫静态成员。静态成员分为静态数据成员和静态函数成员两种。
静态数据成员定义
class node{
public:
static int id;//静态数据成员定义
}
int node::id=10;//静态数据成员类外初始化
静态数据成员的特点
- 类中的静态数据成员,所有对象都共享该数据,只有一份内存在内存中
- 类中的静态数据成员,必须要在类外初始化,因为它不属于对象,而是属于类。对象不管是否存在,这个静态数据成员都是存在的,而且静态数据成员的生命周期是程序开始就存在(主函数运行之前),直到程序结束才会被释放
- 类中的静态数据成员,可以在类中被重新赋值,可以被普通函数访问,如果该成员是公有属性,那么还可以在类外被对象自己访问(没什么意义),或者通过类名访问
静态函数成员定义
class node{
public:
static void fun(){}//在类中定义
static void fun1();//在类中声明
}
void node::fun1(){}//在类外定义
静态函数成员的特点
- 类中的静态函数成员,这个函数同样也不属于对象,而是属于类的,所以在这个函数中不能操作类中的普通数据成员和普通成员函数。因为这些普通成员是必须要有对象的时候才会被建立,而静态函数不用对象也能调用
- 访问和静态数据成员一致
- 可以在这个静态函数中使用局部变量、形参、静态数据成员
示例:
#include<iostream>
struct Entity {
int x, y;
void print() {
std::cout << x << "," << y << std::endl;
}
};
int main() {
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 5;
e1.y = 8;
e.print();//2,3
e1.print();//5,8
}
将x和y变为静态的:static int x, y;
并在结构体下面定义静态变量:
int Entity::x;
int Entity::y;
输出:
5,8
5,8
e1.x = 5;
像这样引用没什么意义
可以像这样引用:Entity::x = 5;
同样地,将print函数改为静态的
e.print();
可以写成Entity::print();
也就是说我们没有必要实例化对象就能使用静态变量和方法
那么,上面的代码可以改为
#include<iostream>
struct Entity {
static int x, y;
static void print() {
std::cout << x << "," << y << std::endl;
}
};
int Entity::x;
int Entity::y;
int main() {
Entity::x = 6;
Entity::y = 7;
Entity::print();
}
上面我们说它们属于类,是指它们是类中的一部分,实质上它们和在命名空间中一样
如果我们让方法保持静态,变量不为静态,改成下面这样:
#include<iostream>
struct Entity {
int x, y;
static void print() {
std::cout << x << "," << y << std::endl;
}
};
int main() {
Entity e;
e.x = 6;
e.y = 7;
Entity::print();
}
出现报错:对非静态成员“Entity::x”的非法引用
你不能从静态方法访问它,因为静态方法没有类实例
局部静态(Local Static)
静态局部变量允许我们声明一个变量,它的生命周期基本上相当于整个程序的生命周期,但是它的作用域范围是被限制的。你可以在任何作用域中声明,如果在函数中声明,那么它的作用域范围被限制在这个函数中。
示例:
#include<iostream>
void Function() {
static int i = 0;
}
int main() {
Function();
}
输出:
1
2
3
4
5
这意味着当我第一次调用函数时,这个变量将被初始化为0,然后所有对函数的后续调用实际上不会创建一个全新的变量
而下面这种情况就不会这样
#include<iostream>
void Function() {
int i = 0;
i++;
std::cout << i << std::endl;
}
int main() {
Function();
Function();
Function();
Function();
Function();
}
输出:
1
1
1
1
1
改变作用域范围也会达到同样的效果
#include<iostream>
int i = 0;
void Function() {
i++;
std::cout << i << std::endl;
}
int main() {
Function();
Function();
Function();
Function();
Function();
}
输出:
1
2
3
4
5
但这种方法的问题是我可以在任意地方访问i
Function();
i=10;
Function();
Function();
Function();
Function();
输出:
1
11
12
13
14
示例——单例类
#include<iostream>
class Singleton {
private:
static Singleton* s_Instance;
public:
static Singleton& Get() {
return *s_Instance;
}
void Hello() {
}
};
Singleton* Singleton::s_Instance = nullptr;
int main() {
Singleton::Get().Hello();
}
我们可以简化它
#include<iostream>
class Singleton {
private:
static Singleton* s_Instance;
public:
static Singleton& Get() {
static Singleton instance;
return instance;
}
void Hello() {
}
};
int main() {
Singleton::Get().Hello();
}
这里就是作为例子,可以进一步了解:【C++】单例模式
static的五种用法
- 全局变量前加static修饰为文件作用域全局变量,内存在全局数据区
- 块作用域前加static修饰为块作用域变量,但内存在全局数据区
- 普通函数前加static修饰为文件作用域函数
- 类中数据成员前加static修饰为类中所有对象共享数据
- 类中函数成员前加static修饰为类中所有对象共享成员