一、C语言的static变量和static函数
首先回顾一下C语言的static变量用法。
#include <iostream>
using namespace std;
//用static修饰变量使变量的状态保留
//因为静态变量只能被初始化一次
void GetStatic()
{
int a = 10;
//b只初始化一次
static int b = 10;
printf("a:%d b:%d \n", a, b);
a++;
b++;
}
int main()
{
GetStatic();
GetStatic();
}
打印结果:
a:10 b:10
a:10 b:11
C语言的静态函数是修饰函数的作用域,static修饰的函数具有文件作用局,无法通过extern在另一个文件访问被static修饰的函数
二、C++static成员变量定义
static成员的定义:static成员需要在类体内声明在类定义体外进行初始化与定义
特殊整型static const成员:
整型static const成员可以在类定义体中初始化,该成员可以不在类体外进行定义
三、C++的static成员变量使用
对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量。比如说统计某种类型对象已创建的数量。如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这时我们可以用类的静态成员来解决这个问题。非static数据成员存在于类类型的每个对象中,static数据成员独立该类的任意对象存在,它是与类关联的对象,不与类对象关联。也就是说,对于static变量来说,所有的对象共享这个变量。
例子如下:
#include <iostream>
using namespace std;
class X
{
public:
char ch;
int static s; //这里是static s的声明,s必须在类外定义!!
};
int X::s = 0;//s的定义
int main()
{
X a,b;
a.s = a.s+10; //对a的s加10
cout<<b.s<<endl;//打印b的s
}
结果:10,可见对象是共享static成员变量的。对于这个例子,我们可以这样理解它的内存模型:
static成员优点如下:
- static成员的名字是在类的作用域中,因此可以避免与其它类成员或全局对象名字冲突。
- 可以实施封装,static成员可以是私有的,而全局对象不可以
- 阅读程序容易看出static成员与某个类相关联,这种可见性可以清晰地反映程序员的意图,增加代码可读性。
- static成员函数没有this指针(因为static成员函数属于类,不属于某个对象)
- 非静态成员函数可以访问静态成员(因为static成员函数属于类,任何此类对象都可以使用)
- 静态成员函数不可以访问非静态成员(非静态成员函数属于具体某个对象,必须通过对象调用成员函数,但是static成员函数不存在对象,所以无法调用非静态函数)
#include <iostream>
using namespace std;
class Test
{
public:
Test(int y) : y_(y){}
~Test(){}
void TestFun()
{
cout<<"x="<<x_<<endl; //OK,非静态成员函数可以访问静态成员
TestStaticFun();
}
static void TestStaticFun()
{
cout<<"TestStaticFun ..."<<endl;
//TestFun(); Error,静态成员函数不能调用非静态成员函数
//cout<<"y="<<y_<<endl; Error,静态成员函数不能访问非静态成员
}
static int x_; // 静态成员的引用性说明
int y_;
};
int Test::x_ = 100; // 静态成员的定义性说明
int main(void)
{
cout<<sizeof(Test)<<endl;
}
打印结果:4。因为static成员变量放在全局变量区,不占用类存储空间。
五、类、对象大小计算
- 类大小计算遵循前面学过的结构体对齐原则
- 类的大小与数据成员有关与成员函数无关
- 类的大小与静态数据成员无关
- 虚函数对类的大小的影响
- 虚继承对类的大小的影响
C++成员函数放在存储于代码段中,通过this指针区分不同对象的访问,不占用对象的存储空间。静态成员变量存储于全局数据区中,也
不占用对象的存储空间。虚继承或者虚函数会跟对象增加一个虚基表指针和虚表指针,影响对象的内存布局,具体如何影响的,留到下片文章吧。