商城网站设计说明书,视频网站做视频节目赚钱吗,东莞网站建设团队全网天下,网站多少钱Static关键字和全局变量 1. 生命周期、作用域和初始化时机2. 全局变量3. Static 关键字3.1 面向过程3.1.1 静态全局变量3.1.2 静态局部变量#xff08;单例中会使用#xff09;3.1.3 静态函数 3.2 面向对象3.2.1 类内静态成员变量3.2.2 类内静态成员函数 Reference 写在前面单例中会使用3.1.3 静态函数 3.2 面向对象3.2.1 类内静态成员变量3.2.2 类内静态成员函数  Reference  写在前面  如果您只是想回顾或了解一下static和全局变量的异同点那么下面的总结将满足您的需求。  如果您是一位初学者或对二者的使用模棱两可建议您读完该篇文章相信这一篇将解答完您对于static关键字以及全局变量的所有疑惑。  如有问题或建议欢迎评论 or 私信 or Contact mejerome.zhounio.com  Static关键字和全局变量异同点  存储位置全局变量和静态变量都存储在内存的静态存储区。  生命周期全局变量和静态变量的生命周期都是整个程序的执行期间。  作用域 全局变量全局变量在整个程序中都是可见的可以在任何函数中访问。静态变量静态变量的作用域仅限于声明它的函数或文件。如果在函数内部声明静态变量那么该静态变量只在该函数内部可见如果在文件内部声明静态变量那么该静态变量只在该文件内部可见。  初始化 全局变量如果没有初始化编译器会自动初始化为0。静态变量如果没有初始化编译器也会自动初始化为0。类内成员变量需要类内声明类外定义定义时可以使用默认初始化  声明方式 全局变量全局变量在所有函数外部声明。静态变量在变量类型前加上关键字static来声明静态变量可在任意位置。  使用场景 全局变量当需要在多个函数或文件中共享数据时可以使用全局变量。静态变量当需要保持变量的值在函数调用之间不变时可以使用静态变量。  总的来说全局变量和静态变量都有全局的生存期但是静态变量的作用域可以是局部的因此静态变量相对于全局变量来说可以更好地保护数据减少了数据被误操作的风险。 1. 生命周期、作用域和初始化时机 
生命周期变量从定义到销毁的时间范围。堆内存和栈内存章节介绍了一下程序的内存管理有一部分是Global Segment全局段负责存储静态变量和全局变量存储在全局段的变量生命周期等于整个程序的运行时间。 作用域 变量的可见代码区域块作用域、函数作用域、类作用域和全局作用域。不管是在全局还是局部声明的static变量都存放在程序的全局段但是它的作用域却不等同于生命周期它的作用域决定于它所被定义的位置。 
从上述两点可以发现静态变量和全局变量具有相同的全生命周期存储在内存的静态存储区但是二者的作用域却不尽相同。 
初始化时机全局变量、文件域中的静态变量、类中的成员静态变量在main函数执行前初始化饿汉式局部变量中的静态变量在第一次调用时初始化懒汉式。 在类中声明的静态成员变量还必须要在类外定义下才可以使用否则会编译报错。 静态成员变量不属于任何一个对象对象的数据中不应该包含静态成员的数据。所以在定义类的时候不会给静态变量分配内存只是声明因此就要在其他地方分配即定义。 定义与声明的区别  声明向编译器声明或介绍了变量的名称类型或者函数及其参数类型等信息。声明并不分配存储空间。你可以多次声明同一个变量。  定义它除了向编译器声明或介绍了变量的名称类型等信息外还分配了存储空间。换句话说定义是声明的扩展。你只能一次定义一个变量。也可以顺便初始化。程序中变量有且只有一个定义更能说明为什么要在类外再定义下类的静态成员变量了。  在C中如果你试图多次定义同一个变量编译器会报错。但是你可以多次声明同一个变量只要它在某个地方被定义过一次。 所以所有的定义包括变量和函数都是声明但并非所有的声明都是定义。 2. 全局变量 
全局变量是在所有函数外部定义的变量其作用域是从定义点开始到文件结尾。全局变量可以被任何函数访问和修改即全局变量的生命周期是整个程序的执行期间。 
在C中全局变量的声明和使用主要有以下几点需要注意 声明全局变量全局变量在所有函数之外定义通常是在程序的开头。例如int x;  使用全局变量在任何函数中直接使用变量名即可访问全局变量。例如x  10;  如果全局变量和局部变量同名那么在局部作用域中局部变量会覆盖全局变量。如果想在局部作用域内访问全局变量需要使用::全局作用域解析运算符。例如::x  20;  在一个文件中定义的全局变量如果想在另一个文件中使用需要在另一个文件中用关键字 extern 来声明该全局变量。例如extern int x; 其他文件不能再定义一个与其相同名字的变量了否则编译器会认为它们是同一个变量。  尽量避免使用全局变量因为全局变量会破坏数据的局部性使得程序的理解和调试变得困难同时也增加了数据的不安全性。  
示例 
假设我们有两个文件一个是main.cpp一个是other.cpp。 
在main.cpp文件中我们定义了一个全局变量并在main函数中使用该全局变量。 
// main.cpp
#include iostream
#include other.cppint x  10;  // 全局变量的定义int main() {std::cout  x  std::endl;  // 使用全局变量printX(); // 其他文件使用全局变量  return 0;
}在other.cpp文件中我们想要使用main.cpp中定义的全局变量x因此其他文件中需要用extern关键字声明该全局变量。 
// other.cpp
#include iostreamextern int x;  // 全局变量的声明void printX() {std::cout  x  std::endl;  // 使用全局变量
}如果我们有一个局部变量和全局变量同名那么在这个局部作用域中局部变量会覆盖全局变量。 
// main.cpp
#include iostreamint x  10;  // 全局变量的定义int main() {int x  20;  // 局部变量的定义std::cout  x  std::endl;  // 输出20因为这里的x指的是局部变量std::cout  ::x  std::endl;  // 输出10使用::访问全局变量return 0;
}3. Static 关键字 
接下来是static关键字笔者按照面向过程不引入类的概念和面向对象类内的static两个角度展开static关键字的使用。 
3.1 面向过程 
3.1.1 静态全局变量 
在全局变量前加上关键字static该变量就被定义成为一个静态全局变量(相当于限制作用域的全局变量作用域仅为当前文件内)。 
#include iostream 
using namespace std;void fn();
static int n; // 定义静态全局变量默认初始化为0int main()
{n  20; // 全局静态变量赋值fn();fn();return 0;
} void fn()
{n;cout  n  endl;
}  2122静态全局变量有以下特点 
全生命周期该变量在Global Segment中分配内存默认初始化未经初始化的静态全局变量会被程序自动初始化为0自动变量的自动初始化值是随机的作用域有限 静态全局变量在声明它的整个文件都是可见的而在文件之外是不可见的静态变量都在全局数据区分配内存包括后面将要提到的静态局部变量。对于一个完整的程序在内存中的分布情况如下【代码区】【全局数据区】【堆区】【栈区】一般程序的由new产生的动态数据存放在堆区函数内部的自动变量存放在栈区静态数据即使是函数内部的静态局部变量存放在全局数据区。自动变量一般会随着函数的退出而释放空间而全局数据区的数据并不会因为函数的退出而释放空间。 
static int n; //定义静态全局变量改为 
int n; //定义全局变量程序照样正常运行。 定义全局变量就可以实现变量在文件中的共享但定义静态全局变量还有以下好处 
静态全局变量不能被其它文件所用其它文件中可以定义相同名字的变量不会发生冲突 
将上述示例代码改为如下 
#include iostream.h void fn();
static int n; //定义静态全局变量 int main()
{n20;cout  n endl;fn();return 0
} //File2 
#include iostream.h 
extern int n;
void fn()
{n;cout  n  endl;
} 编译并运行会发现上述代码可以分别通过编译但运行时出现错误。 这就是因为静态全局变量不能被其它文件所用即使在其它文件中使用extern 进行声明也不行。 
将 
static int n; //定义静态全局变量改为 
int n; //定义全局变量再次编译运行程序程序可正常运行。 
因此在一个文件中静态全局变量和全局变量功能相同而在两个文件中要使用同一个变量则只能使用全局变量而不能使用静态全局变量。 
3.1.2 静态局部变量单例中会使用 
感兴趣的小伙伴可以看一下单例。 
在局部变量前加上关键字static该变量就被定义成为一个静态局部变量(作用域更狭窄的全局变量全生命周期局部变量的作用域)。 
//Example 3
#include iostreamvoid fn();
void fn_local();
int main()
{fn(); //10fn(); //11fn(); //12fn_local(); //10fn_local(); //10return 0;
}void fn()
{static int n  10;std::cout  n  std::endl;n;
}void fn_local()
{int n  10;std::cout  n  std::endl;n;
}在函数fn_local()定义了一个变量每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体系统就会收回栈内存局部变量也相应失效。 
但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来**变量已经不再属于函数本身了不再仅受函数的控制**这给程序的维护带来不便。 
静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区而不是保存在栈中每次的值保持到下一次调用直到下次赋新值。 
静态局部变量有以下特点 
静态局部变量在全局数据区分配内存静态局部变量在程序执行到该对象的声明处时被首次初始化即以后的函数调用不再进行初始化静态局部变量一般在声明处初始化如果没有显式初始化会被程序自动初始化为0静态局部变量始终驻留在全局数据区直到程序运行结束。但其作用域为局部作用域当定义它的函数或语句块结束时其作用域随之结束 
3.1.3 静态函数 
在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同它只能在声明它的文件当中可见不能被其它文件使用。 
#include iostream
static void fn();//声明静态函数int main()
{fn();return 0;
}void fn()//定义静态函数
{int n  10;std::cout  n  std::endl;
}定义静态函数的好处类似于静态全局变量 
静态函数不能被其它文件所用其它文件中可以定义相同名字的函数不会发生冲突 
3.2 面向对象 
3.2.1 类内静态成员变量 
在类内成员变量的声明前加上关键字static该数据成员就是类内的静态数据成员。 
#include iostreamclass Myclass
{
public:Myclass(int a, int b, int c);void GetSum();private:int a, b, c;static int Sum; // 声明静态数据成员 important 此处仅进行了声明
};// 没有类对象时作用域就可见。
int Myclass::Sum  0; // 定义并初始化静态数据成员类外初始化const static 可以类内初始化Myclass::Myclass(int a, int b, int c)
{this-a  a; // 同名变量赋值需要用this指针this-b  b;this-c  c;Sum  a  b  c;
}void Myclass::GetSum()
{std::cout  Sum  Sum  std::endl;
}int main()
{Myclass M(1, 2, 3);M.GetSum();Myclass N(4, 5, 6);N.GetSum();M.GetSum();return 0;
}静态成员变量有以下特点 
静态成员变量是该类的所有对象所共有的。对于普通成员变量每个类对象都有自己的一份拷贝。而静态成员变量一共就一份无论这个类的对象被定义了多少个静态成员变量只分配一次内存由该类的所有对象共享访问。所以静态数据成员的值对每个对象都是一样的它的值可以更新因为静态数据成员在全局数据区分配内存由本类的所有对象共享所以它不属于特定的类对象不占用对象的内存而是在所有对象之外开辟内存在没有产生类对象时其作用域就可见类作用域。因此在没有类的实例存在时静态成员变量就已经存在我们就可以操作它静态成员变量存储在全局数据区。static 成员变量的内存空间既不是在声明类时分配也不是在创建对象时分配而是在定义初始化时分配。静态成员变量必须初始化而且只能在类体外进行。否则编译能通过链接不能通过。在示例中语句int Myclass::Sum0;是定义并初始化静态成员变量。初始化时可以赋初值也可以不赋值。如果不赋值那么会被默认初始化一般是 0 (int Myclass::Sum;)。静态数据区的变量都有默认的初始值而动态数据区堆区、栈区的变量默认是垃圾值。static 成员变量和普通 static 变量一样编译时在静态数据区分配内存到程序结束时才释放。这就意味着static 成员变量不随对象的创建而分配内存也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存在对象销毁时释放内存。静态数据成员初始化与一般数据成员初始化不同。初始化时可以不加 static但必须要有数据类型。被 private、protected、public 修饰的 static 成员变量都可以用这种方式初始化。静态数据成员初始化的格式为数据类型类名::静态数据成员名值类的静态成员变量访问形式1 (类已经被实例化)类对象名.静态数据成员名类的静态成员变量访问形式2类类型名::静态数据成员名也即静态成员不需要通过对象就能访问。静态数据成员和普通数据成员一样遵从public,protected,private访问规则如果静态数据成员的访问权限允许的话即public的成员可在程序中按上述格式来引用静态数据成员 sizeof 运算符不会计算 静态成员变量。 
class CMyclass{int n;static int s;
};    //则sizeofCMyclass等于4何时采用静态数据成员 设置静态成员变量和函数这种机制的目的是将某些和类紧密相关的全局变量和函数写到类里面看上去像一个整体易于理解和维护。如果 想在同类的多个对象之间实现数据共享又不要用全局变量那么就可以使用静态成员变量。也即静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类每个实例的利息都是相同的。所以应该把利息设为存款类的静态数据成员。这有两个好处 不管定义多少个存款类对象利息数据成员都共享分配在全局数据区的内存节省存储空间。一旦利息需要改变时只要改变一次则所有存款类对象的利息全改变过来了。 你也许会问用全局变量不是也可以达到这个效果吗 同全局变量相比使用静态数据成员有两个优势 静态成员变量没有进入程序的全局命名空间因此不存在与程序中其它全局命名冲突的可能不会污染命名空间。可以实现信息隐藏。静态成员变量可以是private成员而全局变量不能。 3.2.2 类内静态成员函数 
静态成员函数为类服务而不是为某一个类的具体对象服务。静态成员函数与静态成员变量一样都是类的内部实现属于类定义的一部分。普通成员函数必须具体作用于某个对象而静态成员函数并不具体作用于某个对象。 
普通的成员函数一般都隐含了一个 this指针(可以参见文章C中的this指针和Python中的Self)this指针指向类的对象本身指向实例化后的对象的地址因为普通成员函数总是具体地属于类的某个具体对象的。当函数被调用时系统会把当前对象的起始地址赋给 this 指针。通常情况下this是缺省的。如函数fn()实际上是this-fn()。 
与普通函数相比静态成员函数属于类本身而不作用于对象因此它不具有this指针。正因为它没有指向某一个对象所以它无法访问属于类对象的非静态成员变量和非静态成员函数它只能调用其余的静态成员函数和静态成员变量。从另一个角度来看由于静态成员函数和静态成员变量在类实例化之前就已经存在可以访问而此时非静态成员还是不存在的因此静态成员不能访问非静态成员。 
#include iostream
using namespace std;class Student{
private:char *name;int age;float score;static int num;  	//学生人数static float total;  //总分
public:Student(char *, int, float);void say();static float getAverage();  //静态成员函数用来获得平均成绩
};int Student::num  0;
float Student::total  0;Student::Student(char *name, int age, float score)
{this-name  name;this-age  age;this-score  score;num;total  score;
}void Student::say()
{coutname的年龄是 age成绩是 score当前共num名学生endl;
}
// 类内静态成员函数智能访问类内静态成员变量
float Student::getAverage()
{return total / num;
}int main()
{(new Student(小明, 15, 90))-say();(new Student(李磊, 16, 80))-say();(new Student(张华, 16, 99))-say();(new Student(王康, 14, 60))-say();cout平均成绩为 Student::getAverage()endl;return 0;
} 小明的年龄是 15成绩是 90当前共1名学生
李磊的年龄是 16成绩是 80当前共2名学生
张华的年龄是 16成绩是 99当前共3名学生
王康的年龄是 14成绩是 60当前共4名学生
平均成绩为 82.25静态成员函数的特点 
出现在类外的函数定义时不能指定关键字static静态成员之间可以相互访问即静态成员函数仅可以访问静态成员变量、静态成员函数、静态变量静态成员函数不能访问非静态成员函数和非静态成员变量非静态成员函数可以任意地访问静态成员函数和静态数据成员由于没有this指针的额外开销静态成员函数与类的全局函数相比速度上会稍快调用静态成员函数两种方式 
通过成员访问操作符(.)和(-)也即通过类对象或指向类对象的指针调用静态成员函数。直接通过类来调用静态成员函数。类名::静态成员函数名参数表。也即静态成员不需要通过对象就能访问。 
Reference 
https://zhuanlan.zhihu.com/p/37439983