关键字 static
是一个在C中具有多种含义的关键词,我觉得很困惑,而且我永远无法理解它实际上应该如何工作 .
根据我的理解,存储持续时间为 static
,这意味着它在全局的情况下持续程序的生命周期,但是默认情况下初始化为零时为're talking about a local, it means that it' .
C标准为具有关键字 static
的类数据成员说明了这一点:
3.7.1 Static storage duration [basic.stc.static]
3关键字static可用于声明具有静态存储持续时间的局部变量 . 4应用于类定义中的类数据成员的关键字static为数据成员提供静态存储持续时间 .
局部变量是什么意思?这是一个函数局部变量吗?因为还有一个当你声明一个本地函数 static
时它只被初始化一次,它第一次进入这个函数 .
它还只讨论了关于类成员的存储持续时间,它是非实例特定的,这也是 static
不是的属性?或者是存储持续时间?
那么 static
和文件范围的情况呢?是否所有全局变量都被视为默认具有静态存储持续时间?以下(来自第3.7.1节)似乎表明:
1所有没有动态存储持续时间,没有线程存储持续时间且不是本地的变量都具有静态存储持续时间 . 这些实体的存储应持续该计划的持续时间(3.6.2,3.6.3)
static
如何与变量的链接相关?
整个 static
关键字是彻头彻尾的混乱,有人可以澄清英语的不同用途,并告诉我何时初始化 static
类成员?
9 回答
变量:
static
变量存在于其定义的翻译单元的"lifetime"中,并且:如果从任何其他翻译单元访问's in a namespace scope (i.e. outside of functions and classes), then it can' . 这被称为"internal linkage"或"static storage duration" . (不要' do this in headers, it'只是一个可怕的想法,你最终在每个翻译单元中都有一个单独的变量,这是一个疯狂的混乱)
如果它是函数中的变量,则无法从函数外部访问它,就像任何其他局部变量一样 . (这是他们提到的当地人)
由于
static
,类成员没有限制范围,但可以从类和实例(如
std::string::npos
)进行寻址 . [注意:您可以在类中声明静态成员,但它们通常仍应在翻译单元(cpp文件)中定义,因此,每个类只有一个]位置代码:
在执行翻译单元中的任何功能之前(可能在
main
开始执行之后),该翻译单元中具有静态存储持续时间(命名空间范围)的变量将是"constant initialized"(在可能的情况下为constexpr
,否则为零),然后是非本地的是"dynamically initialized"正确按照它们在翻译单元中定义的顺序(对于像std::string="HI";
这样不是constexpr
的东西) . 最后,函数本地静态将在第一次执行时被初始化"reaches"它们被声明的行 . 所有static
变量都以与初始化相反的顺序销毁 .实现所有这一切的最简单方法是将所有非
constexpr
初始化的静态变量初始化为函数静态局部变量,这样可以确保在尝试使用它们时正确初始化所有静态/全局变量,从而阻止了static initialization order fiasco .要小心,因为当规范说命名空间范围变量默认为"static storage duration"时,它们意味着"lifetime of the translation unit"位,但这并不意味着它不能在文件外部访问 .
功能
显而易见,
static
经常被用作类成员函数,并且很少用于独立函数 .静态成员函数与常规成员函数的不同之处在于,它可以在没有类实例的情况下调用,并且由于它没有实例,因此它无法访问类的非静态成员 . 当你想要一个绝对不会引用任何实例成员的类的函数或管理
static
成员变量时,静态变量很有用 .static
自由函数意味着该函数不会被任何其他转换单元引用,因此链接器可以忽略它完全 . 这有几个目的:可以在cpp文件中使用,以保证该函数永远不会从任何其他文件中使用 .
可以放在 Headers 中,每个文件都有自己的函数副本 . 没用,因为内联几乎完全相同 .
通过减少工作来加快链接时间
可以在每个翻译单元中放置一个具有相同名称的函数,它们都可以做不同的事情 . 例如,您可以在每个cpp文件中放置一个
static void log(const char*) {}
,它们都可以以不同的方式登录 .静态存储持续时间意味着变量在程序的生命周期内驻留在内存中的相同位置 .
链接与此正交 .
I think this is the most important distinction you can make. 理解这一点,其余的,以及记住它,应该变得容易(不是直接解决@Tony,而是将来可能会读到这个的人) .
关键字
static
可用于表示内部链接 and 静态存储,但实质上这些是不同的 .是 . 无论何时初始化变量(在第一次调用函数时以及执行路径到达声明点时),它都将在程序的生命周期内驻留在内存中的相同位置 . 在这种情况下,
static
为其提供静态存储 .是的,根据定义,所有全局变量都具有静态存储持续时间(现在我们已经清除了这意味着什么) . But 命名空间作用域变量未使用
static
声明,因为这会给它们内部链接,因此每个转换单元都有一个变量 .它为命名空间范围的变量提供内部链接 . 它为成员和局部变量提供静态存储持续时间 .
让我们扩展所有这些:
当然,除非你熟悉它 . :)试图避免在语言中添加新的关键词,委员会重新使用了这个,IMO,这种效果 - 混乱 . 它用来表示不同的东西(可能我会说,可能是反对的东西) .
它实际上非常简单 . 如果在函数范围内将变量声明为static,则在对该函数的连续调用之间保留其值 . 所以:
将显示
678
而不是666
,因为它会记住递增的值 .至于静态成员,它们在类的实例中保留它们的值 . 所以下面的代码:
将打印4,因为first.a和second.a基本上是相同的变量 . 至于初始化,请参阅this question.
为了澄清这个问题,我宁愿用三种不同的形式对'static'关键字的用法进行分类:
(A). variables
(B). functions
(C). member variables/functions of classes
每个小 Headers 的解释如下:
(A) 'static' keyword for variables
这个可能有点棘手,但如果解释和理解得当,它非常简单 .
为了解释这一点,首先了解变量的 scope, duration and linkage 非常有用,没有这些变量总是难以通过staic关键字的模糊概念看到
1. Scope :确定文件中的哪个位置可以访问该变量 . 它可以有两种类型:(i) Local or Block Scope . (ii) Global Scope
2. Duration :确定何时创建和销毁变量 . 同样,它有两种类型:(i) Automatic Storage Duration (对于具有Local或Block范围的变量) . (ii) Static Storage Duration (对于具有全局范围或局部变量的变量(在函数或代码块中)和静态说明符) .
3. Linkage :确定是否可以在另一个文件中访问(或链接)变量 . 再次(幸运的是)它有两种类型:(i) Internal Linkage (对于具有块范围和全局范围/文件范围/全局命名空间范围的变量)(ii) External Linkage (对于仅具有全局范围/文件范围/全局命名空间的变量)范围)
让我们参考下面的例子来更好地理解普通的全局和局部变量(没有静态存储持续时间的局部变量):
现在出现了Linkage的概念 . 当在一个文件中定义的全局变量打算在另一个文件中使用时,变量的链接起着重要作用 .
全局变量的链接由关键字指定:(i) static ,和(ii) extern
(现在你得到解释;-))
static关键字可以应用于具有本地和全局范围的变量,在这两种情况下,它们意味着不同的东西 . 我将首先解释在具有全局范围的变量中使用'static'关键字(其中我还阐明了关键字'extern'的用法),然后解释了具有局部范围的变量 .
1. Static Keyword for variables with global scope
全局变量具有静态持续时间,这意味着当使用它的特定代码块(例如main())结束时,它们不会超出范围 . 根据链接,它们只能在声明它们的同一文件中访问(对于静态全局变量),或者在文件外部甚至在声明它们的文件之外(外部类型全局变量)访问它们 .
在具有extern说明符的全局变量的情况下,并且如果在初始化它的文件之外访问该变量,则必须在正在使用它的文件中向前声明它,就像函数必须是向前的一样 . 声明它的定义是否在与使用它的位置不同的文件中 .
相反,如果全局变量具有static关键字,则不能在已声明它的文件之外使用它 .
(见下面的例子澄清)
例如:
main3.cpp
现在c中的任何变量都可以是const或非const,对于每个'const-ness',我们得到两个默认c链接的情况,如果没有指定:
(i) If a global variable is non-const, its linkage is extern by default ,即非const全局变量可以通过使用extern关键字的前向声明在另一个.cpp文件中访问(换句话说,非const全局变量具有外部链接(当然具有静态持续时间)) . 在原始文件中使用extern关键字也是多余的 . 在这种情况下 to make a non-const global variable inaccessible to external file, use the specifier 'static' before the type of the variable .
(ii) If a global variable is const, its linkage is static by default ,即const全局变量不能在除定义之外的文件中访问(换句话说,const全局变量具有内部链接(当然具有静态持续时间)) . 还使用static关键字来防止在另一个文件中访问const全局变量是多余的 . 在这里, to make a const global variable have an external linkage, use the specifier 'extern' before the type of the variable
以下是具有各种链接的全局范围变量的摘要
接下来,我们将研究在不同文件中访问时上述全局变量的行为方式 .
2. Static Keyword for variables with Local Scope
之前我提到过具有局部范围的变量具有自动持续时间,即它们在输入块时存在(无论是普通块,还是功能块),当块结束时不再存在,长话短说, variables with local scope have automatic duration 和自动持续时间变量(和对象)没有链接意味着它们在代码块之外是不可见的 .
如果 static 说明符应用于块中的局部变量,则 changes the duration of the variable from automatic to static 及其生命周期是程序的整个持续时间,这意味着它具有固定的内存位置,并且其值仅在程序启动之前初始化一次,如cpp reference中所述 . (初始化不应与赋值混淆)
让我们来看一个例子 .
以上是对应用于变量的static关键字的解释 . pheww!
B. 'static' keyword used for functions
在函数方面,static关键字具有直接的含义 . 这里, refers to linkage of the function 通常,在cpp文件中声明的所有函数都默认具有外部链接,即在一个文件中定义的函数可以通过前向声明在另一个cpp文件中使用 .
using a static keyword before the function declaration limits its linkage to internal ,即静态函数不能在其定义之外的文件中使用 .
C. Staitc Keyword used for member variables and functions of classes
1. 'static' keyword for member variables of classes
我在这里直接开始一个例子
在此示例中,静态变量m_designNum保留其值,并且此单个私有成员变量(因为它是静态的)与对象类型DesignNumber的所有变量共享b / w
与其他成员变量一样,类的静态成员变量也不与任何类对象相关联,这可以通过在main函数中打印anyNumber来演示
const vs non-const static member variables in class
(i) non-const class static member variables 在前面的示例中,静态成员(公共和私有)都是非常量 . ISO标准禁止在类中初始化非常量静态成员 . 因此,与前面的示例一样,它们必须在类定义之后初始化,并且需要忽略static关键字
(ii) const-static member variables of class 这很简单,并且符合其他const成员变量初始化的约定,即类的const静态成员变量可以在声明点初始化,它们可以是在类声明结束时初始化,但有一点需要注意,在类定义之后初始化时,需要将关键字const添加到静态成员中 .
但是,我建议在声明点初始化const静态成员变量 . 这符合标准的C约定,使代码看起来更清晰
有关类中静态成员变量的更多示例,请从learncpp.com查看以下链接http://www.learncpp.com/cpp-tutorial/811-static-member-variables/
2. 'static' keyword for member function of classes
就像类的成员变量可以是静态的一样,类的成员函数也是如此 . 类的普通成员函数始终与类类型的对象相关联 . 相反,类的静态成员函数不与类的任何对象相关联,即它们没有* this指针 .
其次,因为类的静态成员函数没有* this指针,所以可以使用main函数中的类名和范围解析运算符来调用它们(ClassName :: functionName();)
第三,类的静态成员函数只能访问类的静态成员变量,因为类的非静态成员变量必须属于类对象 .
有关类中静态成员函数的更多示例,请从learncpp.com查找以下链接
http://www.learncpp.com/cpp-tutorial/812-static-member-functions/
静态变量在类的每个实例之间共享,而不是每个类都有自己的变量 .
'MyClass'的每个实例都有自己的'myVar',但共享相同的'myStaticVar' . 实际上,您甚至不需要MyClass的实例来访问“myStaticVar”,您可以在类之外访问它,如下所示:
当在函数内部用作局部变量(而不是作为类成员变量)时,static关键字会做出不同的事情 . 它允许您创建持久变量,而不提供全局范围 .
就持久性而言,它是一个全局变量......但没有全局范围/可访问性 .
您还可以拥有静态成员函数 . 静态函数基本上是非成员函数,但在类名的命名空间内,并且具有对类成员的私有访问权 .
当你调用一个成员函数时,有's a hidden parameter called ' this',这是一个指向调用该函数的类实例的指针 . 静态成员函数 don't 具有该隐藏参数...它们可以在没有类实例的情况下调用,但也无法访问类的非静态成员变量,因为它们在任何特定的类实例上都不会被调用' pointer to work with. They aren' .
当您在文件范围声明
static
变量时,该变量仅在该特定文件中可用(从技术上讲,*转换单元,但不要让它太复杂) . 例如:a.cpp
b.cpp
main.cpp:
对于局部变量,
static
表示该变量将被零初始化并在调用之间保留其值:对于类变量,它意味着该类的所有成员之间只共享该变量的单个实例 . 根据权限,可以使用其完全限定名称从类外部访问该变量 .
将非类函数标记为
static
使得该函数只能从该文件访问,并且无法从其他文件访问 .a.cpp
b.cpp
对于类成员函数,将它们标记为
static
意味着该函数不具有this
指针 .我不是C程序员,因此我无法正确地向您提供有关静态在C程序中使用的信息,但是当面向对象编程时,静态基本上声明一个变量,或者一个函数或类是相同的整个计划的整个生命周期 . 举个例子 .
当你在Main中实例化这个类时,你会做这样的事情 .
这两个类实例彼此完全不同,并且彼此独立地操作 . 但是如果你要重新创建这样的A类 .
让我们再回到主要 .
然后a1和a2将共享相同的int x副本,其中a1中x上的任何操作都将直接影响a2中x的操作 . 所以如果我这样做的话
A类的两个实例共享静态变量和函数 . 希望这能回答你的问题 . 我对C的有限知识允许我说将函数或变量定义为static意味着它只对文件可见,函数或变量被定义为静态 . 但是这样可以更好地回答一个C人而不是我 . C允许C和C两种方式将变量声明为静态,因为它完全向后兼容C.
是 - 非全局,例如函数局部变量 .
对 .
也就是说,永远不会复制
R
shareint R::a
-int R::a
的所有实例 .实际上是一个具有构造函数/析构函数的全局 - 初始化在访问之前不会延迟 .
对于本地函数,它是外部的 . 访问:该功能可访问(当然,除非您将其返回) .
对于一个类,它是外部的 . 访问:标准访问说明符适用(公共,受保护,私有) .
static
还可以指定内部链接,具体取决于它的声明位置(文件/命名空间) .它在C中有太多用途 .
它会在
main
之前自动初始化,如果's loaded and has a constructor. That might sound like a good thing, but initialization order is largely beyond your control, so complex initialization becomes very difficult to maintain, and you want to minimize this -- if you must have a static, then function local scales much better across libraries and projects. As far as data with static storage duration, you should try to minimize this design, particularly if mutable (global variables). Initialization ' time'也因多种原因而变化 - 加载器和内核有一些技巧可以最小化内存占用并推迟初始化,具体取决于相关数据 .Static Object: 我们可以使用static关键字定义类成员static . 当我们将类的成员声明为static时,意味着无论创建了多少个类的对象,都只有一个静态成员的副本 .
静态成员由类的所有对象共享 . 如果没有其他初始化,则在创建第一个对象时,所有静态数据都将初始化为零 . 我们不能将它放在类定义中,但它可以在类外部初始化,如下例所示,通过重新声明静态变量,使用范围解析运算符::来标识它所属的类 .
让我们尝试以下示例来理解静态数据成员的概念:
编译并执行上述代码时,会产生以下结果:
Static Function Members: 通过将函数成员声明为static,可以使其独立于类的任何特定对象 . 即使没有类的对象存在,也可以调用静态成员函数,并且仅使用类名和范围解析运算符::来访问静态函数 .
静态成员函数只能访问静态数据成员,其他静态成员函数以及类外部的任何其他函数 .
静态成员函数具有类作用域,并且它们无权访问该类的this指针 . 您可以使用静态成员函数来确定是否已创建类的某些对象 .
让我们尝试以下示例来理解静态函数成员的概念:
编译并执行上述代码时,会产生以下结果: