主要讲解了静态成员的本质以及静态成员和普通成员的差别,通过对象模型的基本知识体现了静态成员函数和普通函数的差别。
类的静态成员
普通成员变量
普通成员变量受到public和private两个关键字的限制,可以通过类的对象名访问具有public的普通成员变量,
普通的成员变量不能够在类的对象之间共享。
静态成员变量
1.在C++中可以定义静态成员变量和静态成员函数,
静态成员属于整个类所有,不需要任何依赖对象。
可以通过类名直接访问public静态成员,可以通过对象名访问public静态成员。
静态成员函数可以直接访问静态成员变量。
2.
普通成员变量依附于具体的对象存在,对象被销毁普通的成员变量就被销毁。在某一个对象诞生的时候静态成员变量不会被创建,同样某一个类被销毁静态成员变量也不会被销毁。它随着类的存在而存在,类是在程序开始运行的时候就存在,所以静态成员变量是随着程序的运行而存在的。
3.静态成员变量不依附于对象的存在而存在,所以
静态成员的空间分配需要在类的外部,也就是在
全局数据区对静态成员变量进行内存的分配。
如果静态成员变量没有被赋予初值,那么静态成员变量和普通的静态变量一样
默认的初始值为0。
4.
静态成员变量在类的内部进行声明,在类的外部进行定义并分配空间。
5.语法规则:在定义时直接通过static关键字进行修饰。语法规则:Type Class Name ::VarName,同时
在类外定义静态成员需要在类的初始化之后,否则编译器无法找到属于这个类的静态成员。
静态成员函数
1.
静态成员函数是属于类所有的,并不是依附于某一个对象而存在,调用静态成员函数直接通过类名调用即可。
2.
静态成员函数可以通过类的对象来进行调用。
基本例程如下所示:
#include <stdio.h>
//int A::ai; 类的静态变量定义在类的前面将会报错
class A
{
private:
static int ai;
public:
static int getai()
{
return ai;
}
static int setai(int i)
{
ai = i;
return 0;
}
void print()
{
printf("ai = %d\n", ai);
}
};
int A::ai = 0;
int main()
{
/*不需要建立类的对象就可以调用类的静态成员函数*/
A::setai(9);
printf("ai = %d\n",A::getai());
/*print函数是类的普通成员函数,需要建立类的对象来完成调用*/
A a;
a.print();
A a1;
/*可以通过类的对象来抵用类的静态成员函数*/
a1.setai(6);
printf("ai = %d\n",a1.getai());
//a1.ai = 8; 虽然可以通过类的对象来改变静态成员变量的值,但是这里的静态成员变量仍然受到public和private属性的限制。
return 0;
}
C++不同角度上的类的静态成员
命名空间的角度:
通过上面的例程,可发现在类的外部定义类的静态成员变量的代码:
int A::ai = 0;
这里的A::ai类似于命名空间,这里的
静态成员变量,只是这个类的范围的命名空间的全局变量。同样,类的静态成员函数就是这个类的命名空间的全局函数。
不同的地方是,类可以对静态成员变量进行访问权限的限制(使用public和private关键字),而命名空间不可以。
面向对象的角度:
类的静态成员属于类的概念本身,类的所有对象共享相同的静态成员。
类的静态成员的应用
类的静态成员依赖于类而存在,所以可以通过静态成员变量来统计程序中正在运行的类。
#include <stdio.h>
class A
{
private:
static int ai;
public:
static int getai()
{
return ai;
}
A()
{
ai++;
}
~A()
{
ai--;
}
};
/*如果不给静态成员变量赋初值,那么ai的默认值为0*/
int A::ai;
void run()
{
A a[100];
/*
这里定义了一个100A类的数组,所以通过调用静态成员函数ai再次进行100次自加,打印102
*/
printf("%d\n",A::getai());
/*run()函数在调用结束后100个A类的数组自动被销毁,调用析构函数,ai自减100*/
}
int main()
{
A a1;
A a2;
/*定义两个A类的对象后,ai通过定义的默认构造函数自加两次,这里打印2*/
printf("%d\n",A::getai());
run();
/*run函数调用结束返回,这个时候静态成员变量的值是2*/
printf("%d\n",A::getai());
return 0;
}
C++对象模型初探
普通成员变量和struct变量的存储
1.C++中的成员变量和成员函数是分开存储的。成员变量分为普通成员变量和静态成员变量,
普通的成员变量存储在对象中,
与struct变量具有相同的内存布局和字节对齐方式。静态成员变量存储在程序的全局数据区。
2.
成员函数存储在代码段中,无论是静态成员函数还是普通的成员函数。
例程:
#include <stdio.h>
/*两种方式在C++定义类的差别在于:A默认的成员是private,B默认的成员是public*/
class A
{
int i;
int j;
short a;
char b;
};
struct B
{
int i;
int j;
short a;
char b;
};
class C
{
private:
int i;
int j;
short a;
char b;
/*静态成员变量占用的全局数据区的内存空间*/
static int h;
public:
/*成员函数占用的代码段的内存空间*/
C()
{
printf("abc\n");
}
int print()
{
i = 1;
return 0;
}
};
int C::h = 0;
int main()
{
/*根据C语言中的内存对齐,A和B结构体占用的内存空间都为12个字节*/
printf ("%d\n",sizeof(A));
printf ("%d\n",sizeof(B));
/*程序的打印结果和结构体A和B的打印结果相同*/
printf ("%d\n",sizeof(B));
return 0;
}
C++中的对象模型
面向对象理论到计算机程序的转化基本程序(这里只是一个简单的例子,具体的实现还是C++编译器内部的实现),如下图所示:
静态成员函数和普通成员函数的差别
1.
普通成员函数不包含指向具体对象的指针,普通成员函数包含一个指向具体对象的指针。
2.
C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
3.
静态成员函数没有this指针,普通成员函数有。普通成员函数通过C++默认的关键字this将当前成员函数的地址提供出来。
例程如下:
#include <stdio.h>
class A
{
private:
int a;
int b;
int c;
static int d;
public:
A(int a, int b, int c)
{
/*如果这里这样定义将会造成二义性,程序最后将无法确定怎么进行赋值,所以这里编译报错*/
/*this->a = a;
this->b = b;
this->c = c;*/
/*这里的this是C++中的关键字,这里的this指针指向当前的对象所以this->a是指向这个类的成员变量a*/
this->a = a;
this->b = b;
this->c = c;
}
void print()
{
/*打印各个变量的值和变量地址*/
printf("a = %d, &a = %p\n",a,&a);
printf("b = %d, &b = %p\n",b,&b);
printf("c = %d, &c = %p\n",c,&c);
/*通过地址打印可以看出d的地址和变量a,b,c的地址并不是连续的,可以进一步证明静态成员变量的内存分配来自全局数据区*/
printf ("d = %d, &c = %p\n",d,&d);
/*这里打印this指针的地址,也是具体的对象的地址,随着对象的创建而this指针式不同的*/
printf("%p\n",this);
}
};
int A::d = 0;
int main()
{
A g = A(2,3,4);
g.print();
printf ("&g = %p\n", &g);
A g2 = A(7,8,9);
g2.print();
printf ("&g = %p\n", &g2);
return 0;
}