概述
C++ 中 Static 关键字的作用
- Static 用在类或者结构体的外部
- Static 用在类或者结构体的内部
- Static 作用于局部(Local)
Static 能够被用在一个类或者结构体的内部或者外部。
如果用在外部的话,意味着被你声明成 static 的变量只能作用于该定义的 Translation Unit.
如果用在内部的话,则意味着该 static 变量将会共享该类所有实例的内存空间。即在所有该类的实例中,该变量将只会有一个实例。如果某一个类的实例改变了该变量的值,则所有的类实例中该变量的值都将被改变。
Static 用在类或者结构体的外部
下面我将用例子来说明:
首先,在两个 cpp 文件 “static.cpp” 和 “main.cpp” 中分别定义同一个变量,其中一个定义为static。
//static.cpp
static int s_Valuable = 5;
//main.cpp
#include <iostream>
int s_Valuable = 10;
int main()
{
std::cout << s_Valuable << std::endl;
std::cin.get();
}
上述代码运行起来不会有任何问题,且输出为 10。
但是如果现在把 “static.cpp” 中变量的 static 去掉呢?
//static.cpp
int s_Valuable = 5;
Error LNK1169 one or more multiply defined symbols found
Error LNK2005 "int s_Valuable" (?s_Valuable@@3HA) already defined in Main.obj
则这时会有 Link 错误产生: 变量 “s_Valuable” 被重复定义。
这是为什么呢?
如上述所说 ,static 定义在类或者结构体外部的变量或者函数只会存在于该定义的 Translation unit 中。 可以狭义的理解为 “static int s_Valuable” 只定义在 “static.cpp” 中, 就像该文件的私有变量一样。所以程序运行时,linker 不会将这两个相同的变量链接起来,因此不会产生 link 错误。
解决这个问题,我们可以在 mian.cpp 中的变量 “s_Valuable” 前面加上关键字 “extern”。
//main.cpp
#include <iostream>
extern int s_Valuable = 10;
int main()
{
std::cout << s_Valuable << std::endl;
std::cin.get();
}
这时程序运行成功,并且输出结果为5. “extern” 关键字的作用为在外部的 Translation Unit 中寻找该变量 “s_Valuable” 的定义。所以输出结果为 “static.cpp” 中该变量的值。这种行为被称作 “external linking” 或者 “external linkage”。
如果这时,在 “static.cpp” 中再加上关键字 “static” 呢?
//static.cpp
static int s_Valuable = 5;
Error LNK2001 unresolved external symbol "int s_Valuable" (?s_Valuable@@3HA)
你又会看到 link error了。但是这次不同的是,Linker 在全局空间里找不到该变量的定义。
当然,上述规则也同样适用于函数。如果你在函数前面加上 static, 跟上述变量的结果是一样的。
另外,如果在一个 “.h” 文件中定义一个 static 函数或者变量呢?
假设你在不同的文件里 include 了这个 “.h” 文件,那么对于每一个文件而言,都相当于把 “.h” 文件中的代码复制到自己的内容里,所以相当于你直接复制了该 static 变量或者函数的定义,因此也不会有问题。
最后总结: 如果你不想要你定义的这个变量是一个全局的变量的话,即可以被该 solution 中所有的文件可见 ,只想要对当前文件或者当前 Translation Unit 可见的话,请将其声明为 static。
Static 用在类或者结构体的内部
还是举例说明:
//main.cpp
#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 = {5, 8};
e.print();
e1.print();
}
这是一个非常简单的程序,“e” 和 “e1” 是结构体 “Entity” 的两个实例,然后分别被初始化为2,3 和 5,8。 很显然,该程序的输出为 2,3 和 5,8.
如果这时,将 “Entity” 中的变量定义为 static 呢?
struct Entity
{
static int x, y;
void print()
{
std::cout << x << ", " << y << std::endl;
}
};
那么首先初始化语句 “Entity e1 = {5, 8}” 会失败,原因是变量 x, y 被声明成 static 之后就不再是类成员变量了。所以我们首先要重新初始化。
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 5;
e1.y = 8;
e.print();
e1.print();
}
运行代码,会出现以下的 Link error.
Error LNK2001 unresolved external symbol "public: static int Entity::y" (?y@Entity@@2HA)
Error LNK2001 unresolved external symbol "public: static int Entity::x" (?x@Entity@@2HA)
Error LNK1120 2 unresolved externals Practice
这些 Error 是告诉我们 x 和 y 必须要被声明。
#include <iostream>
struct Entity
{
static int x, y;
void print()
{
std::cout << x << ", " << y << std::endl;
}
};
int Entity::x;
int Entity::y;
int main()
{
Entity e;
e.x = 2;
e.y = 3;
Entity e1;
e1.x = 5;
e1.y = 8;
e.print();
e1.print();
}
运行结果为: 5,8 和 5,8.
这是因为如上述所说,x,y 如果被声明为 static, 那么变量 x, y将只会有一个实例,所有的 Entity的实例都将共享 x, y的实例。也就是实际上,实例 e 和 e1 的 x, y 指向的是同一块内存空间。 所以即使上上述的代码可以等同于下面:
int main()
{
Entity e;
Entity::x = 2;
Entity::y= 3;
Entity e1;
Entity::x = 5;
Entity::y = 8;
e.print();
e1.print();
}
实际上,可以视为 变量 x, y 被声明在一个 Entity 的命名空间 (namespace)中,它们并不真的属于类或者结构体 。当然,它们依然具有部分类成员的属性,可以被声明为 public 或者 private,但是在该类或者结构体被实例化的时候,static 变量并不会随之开辟新的内存空间。
这个方法在你想要创建一个可以跨越不同的类的变量是非常有用的。当然,你也可以通过创建一个全局变量来达到这个效果,但是不同的是,static的类变量并不会跨越 translation unit.
对于函数来讲,和变量的效果是一样的。
#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 e;
Entity::x = 2;
Entity::y= 3;
Entity e1;
Entity::x = 5;
Entity::y = 8;
e.print();
e1.print();
}
将函数 print() 改成 static 之后,程序没有任何问题。因为 print() 函数中涉及到的变量 x, y 也都是 static 变量。
同时,该方法可以被这样调用:
Entity::print();
Entity::print();
那鉴于 Entity 中所有的变量以及函数都是 static 的,所以我们根本不需要实例化。 那代码可以修改为:
int main()
{
Entity::x = 2;
Entity::y= 3;
Entity::x = 5;
Entity::y = 8;
Entity::print();
}
如果这时我们将变量 x, y 改成非 static, 但依然将print函数声明为 static 呢?
#include <iostream>
struct Entity
{
int x, y;
static 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;
Entity::print();
Entity::print();
}
Error C2597 illegal reference to non-static member 'Entity::x'
很显然,static 方法是不能调用非 static 的变量的。 该Error 为非法调用, 因为static函数是没有类实例的。即当 Entity 被实例化的时候,x, y 会随之被实例化,开辟新的内存空间,但是 static 方法 print 并不会被实例化,即可以狭义理解为该方法并不存在于类实例中 (并不一定完全正确,只是帮助理解用)。实际上每一个非static的类成员函数都会获取一个当前类的实例作为一个参数,可视为一个隐形的参数,但是static的方法并不能获得这个参数。
类中的static 方法等同于在类外面的函数。如果将print类写在 Entity 的外面,则 print 函数根本不知道 x,y 是什么。
struct Entity
{
int x, y;
};
static void print()
{
std::cout << x << ", " << y << std::endl;
}
Error (active) E0020 identifier "y" is undefined
Error (active) E0020 identifier "x" is undefined
如果我们给 print 函数一个类实例参数:
struct Entity
{
int x, y;
};
static void print(Entity e)
{
std::cout << e.x << ", " << e.y << std::endl;
}
int main()
{
Entity e;
e.x = 2;
e.y= 3;
Entity e1;
e1.x = 5;
e1.y = 8;
print(e);
print(e1);
}
这时代码便能够正常运行了。
对于类中的方法来说,实际上也会有一个隐形类实例参数, 如上述所示。而对类中的 static 方法而言,它并不能获取该参数,所以不能识别类中的非 static 的变量。
Static 作用于局部(Local)
首先,定义一个变量我们可以规定了它的作用范围以及生命周期。作用范围意味着哪里可以访问该变量,而生命周期则指该变量会在内存空间中保留多久。Local Static 即表示创建一个生命周期为整个程序,但是作用范围为当前声明该变量的函数,其他的函数并不能访问它 (作用范围可以自己定义)。
依然用例子说明:
#include <iostream>
void Function()
{
int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
for (size_t i = 0; i < 5; i++)
{
Function();
}
std::cin.get();
}
该函数的输出结果很显然为 1,1,1,1,1。
但是如果将 i 定义为 static 呢?
#include <iostream>
void Function()
{
static int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
for (size_t i = 0; i < 5; i++)
{
Function();
}
std::cin.get();
}
输出结果为 1,2,3,4,5. 这时我们想要的结果。
当然,可以将 i 声明为全局变量来达到相同的效果。
#include <iostream>
int i = 0;
void Function()
{
i++;
std::cout << i << std::endl;
}
int main()
{
for (size_t i = 0; i < 5; i++)
{
Function();
}
std::cin.get();
}
但此时该变量可以随时在外部被改变。
#include <iostream>
int i = 0;
void Function()
{
i++;
std::cout << i << std::endl;
}
int main()
{
Function();
i = 10;
for (size_t i = 1; i < 5; i++)
{
Function();
}
std::cin.get();
}
输出结果此时为1,11,12,13,14.
如果你并不想要变量 i 的值能够在任意地方被修改,则可以将 i 在函数内部声明为static。
Reference:https://www.youtube.com/watch?v=f7mtWD9GdJ4&list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb&index=23
最后
以上就是俭朴期待为你收集整理的C++ 中 Static 关键字的作用的全部内容,希望文章能够帮你解决C++ 中 Static 关键字的作用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复