static详情
1.C语言中的static
C语言中的static应用于普通变量和函数。
—变量
修饰全局变量时表明该变量只能在当前文件中使用;
未经初始化的静态全局变量会被程序自动初始化为0。
在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子,如下:
using namespace std;
void fn(); //声明函数
static int n; //声明静态全局变量
int main()
{
n = 20; //为n赋初值
cout<<n<<endl;//输出n的值
fn(); //调用fn函数
}
void fn()
{
n++; //n的值自加一(n=n+1)
cout<<n<<endl; //输出n的值
}
1)全局变量是不显式用 static 修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过 extern 全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。
修饰局部变量时延长该变量的生命周期–和成员声明周期一样,类似该变量具有记忆的功能,存放位置不同于函数体的局部变量。
在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。如下:
#include <iostream>
using namespace std;
void fn();
int main()
{
fn();
fn();
fn();
return 0;
}
void fn()
{
static int n = 10;
cout<<n<<endl;
n++;
}
—函数
修改了函数的链接属性,只能在该文件中使用;
其它文件中可以定义相同名字的函数,不会发生冲突。
举例如下:
#include <iostream>
using namespace std;
static void fn();//声明静态函数
int main()
{
fn();
}
static void fn()//定义静态函数
{
int n = 10;
cout<<n<<endl;
}
2.C++中的static
C++中static叶修是变量,函数,但这里所指的时类中的成员变量和成员函数。
下面写一个求类中创建了多少对象的代码来展示
// 一个类创建了多少个对象 --计数
class Date
{
public:
Date(int year, int month, int day)
//对当前对象中的成员变量进行初始化
: _year(year)
, _month(month)
, _day(day)
{
++_count;
cout << "Date(int,int,int):" << this << endl;
}
Date() {
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
++_count;
cout << "Date(const Date&):" << this << endl;
}
Date& operator=(const Date& d)
{
cout << this << "=" << &d << endl;
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
~Date()
{
_count--;
cout << "~Date():" << this << endl;
}
private:
int _year;
int _month;
int _day;
public:
static int _count;//静态成员变量
};
//int _count = 0; 采用全局变量可以实现,缺陷是可以任意修改
//静态成员变量在类外初始化
int Date::_count = 0;
void TestDate() {
Date d(2021, 1, 1);
}
int main()
{
cout << Date::_count << endl;
Date d1,d2;
cout << sizeof(d1) << endl;
cout << sizeof(d2) << endl;
cout << &d1._count << endl;
cout << &d2._count << endl;
return 0;
}
静态成员变量:没有包含在具体的对象中,是所有对象共享的成员变量,只有一份是类的属性。静态成员变量不会影响sizeof结果,上述类中定义的静态成员变量是public,否则就无法调用,当然,如果将权限改为private,又想调用,该怎么解决,这里就要用到静态成员函数,往下看。
class Date
{
public:
Date(int year, int month, int day)
//对当前对象中的成员变量进行初始化
: _year(year)
, _month(month)
, _day(day)
{
++_count;
cout << "Date(int,int,int):" << this << endl;
}
Date() {
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
++_count;
cout << "Date(const Date&):" << this << endl;
}
Date& operator=(const Date& d)
{
cout << this << "=" << &d << endl;
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
~Date()
{
_count--;
cout << "~Date():" << this << endl;
}
//静态成员函数
static int GetCount() {
return _count;
}
private:
int _year;
int _month;
int _day;
static int _count;//静态成员变量
};
//int _count = 0; 采用全局变量可以实现,缺陷是可以任意修改
//静态成员变量在类外初始化
int Date::_count = 0;
int main()
{
Date d;
d.GetCount(); //对象.成员函数
Date::GetCount(); //类名::成员函数
return 0;
}
上面定义了一个静态成员函数,将静态成员函数作为静态成员的返回值,在主函数中调用静态成员函数,就可以。与上面的静态成员变量的结果一致。
特性:
静态数据成员
(1)静态数据成员可以实现多个对象之间的数据共享,它是类的所有对象的共享成员,它在内存中只占一份空间,如果改变它的值,则各对象中这个数据成员的值都被改变。
(2)静态数据成员是在程序开始运行时被分配空间,到程序结束之后才释放,只要类中指定了静态数据成员,即使不定义对象,也会为静态数据成员分配空间。
(3)静态数据成员可以被初始化,但是只能在类体外进行初始化,若未对静态数据成员赋初值,则编译器会自动为其初始化为 0。
(4)静态数据成员既可以通过对象名引用,也可以通过类名引用。
静态成员函数
(1)静态成员函数和静态数据成员一样,他们都属于类的静态成员,而不是对象成员。
(2)非静态成员函数有 this 指针,而静态成员函数没有 this 指针。
(3)静态成员函数主要用来方位静态数据成员而不能访问非静态成员。
使用方法:
对象.静态成员变量/函数 或 类名::静态成员变量/函数
这里有几个小结论直接告诉大家 ,此处就不做演示了
1.不能通过类名来调用类的非静态成员函数,可以通过类的对象调用静态成员函数和非静态成员函数。
2.类的对象可以使用静态成员函数和非静态成员函数。
3.静态成员函数中不能引用非静态成员,在类的非静态成员函数中使用类的静态成员。
结尾彩蛋
细心的同学应该会发现上面有提到过extern,这里简单介绍一下。
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。extern声明不是定义,即不分配存储空间。
在一个文件中定义了变量和函数, 在其他文件中要使用它们, 可以有两种方式:
1.使用头文件,然后声明它们,然后其他文件去包含头文件
2.在其他文件中直接extern
extern C
C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时无法找到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
比如说你用C 开发了一个DLL 库,为了能够让C ++语言也能够调用你的DLL 输出(Export) 的函数,你需要用extern “C” 来强制编译器不要修改你的函数名。