简述
在结构化程序设计中,内存中数据的共享是通过参数, 全局变量实现的;
在面向对象程序设计中函数与数据成员封装在一起, 数据共享通过类内部数据成员之间的相互访问, 外部对象则通过静态成员(即static成员)来共享数据;
接下来通过面向对象叙述static特性
C++中的static
静态成员
使用关键字static修饰的函数/数据成员, 静态成员属于整个类, 由同一个类的所有对象所拥有,共享; 一般通过类名进行访问: 类名::标识符 ;
注: 在类中定义静态数据成员, 仅仅是对静态数据成员进行引用性声明, 必须在类外使用类名限定定义性声明(即真正的声明),这时才能进行初始化
#include<iostream>
using namespace std;
class A{public:static int count;A(){++count; cout<<"构造函数"<<endl; }A(A const& a){++count; cout<<"复制构造函数"<<endl; }~A(){cout<<"析构函数"<<endl;count--; }static int show(){return count; //静态成员函数可以直接访问该类的静态数据成员,访问费静态数据成员只能通过对象名} };
int A::count=0;
void f(){//顺带测试一下静态生存期static int j=0; j+=2;int i=2;cout<<i<<" static j="<<j<<endl;i+=2;
}
int main(){f();cout<<"A对象个数"<<A::show()<<endl;//还没有构造对象的时候,count数为0A a;A b=a;//发生复制构造 A c(b);A d;cout<<"A对象个数"<<A::count<<endl; //此时count数为4cout<<endl;f(); return 0;
}
此处做着重说明: 静态函数不能是虚函数, 虚函数用于动态绑定(通过虚表的索引值进行函数地址的查找, 索引值必须用到this, 所以静态函数函数不能作为虚函数)
为何static成员函数不能为virtual
1. static成员不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义的。
2. 静态与非静态成员函数之间有一个主要的区别。那就是静态成员函数没有this指针。
虚函数依靠vptr和vtable来处理。vptr是一个指针,在类的构造函数中创建生成,并且只能用this指针来访问它,因为它是类的一个成员,并且vptr指向保存虚函数地址的vtable.
对于静态成员函数,它没有this指针,所以无法访问vptr. 这就是为何static函数不能为virtual.
虚函数的调用关系:this -> vptr -> vtable ->virtual function
第二点: 对const成员函数设置为静态没有任何意义, const只是对函数的this指针进行限制, 而静态函数没有this指针
使用普通指针指向类的静态成员
#include <iostream>
using namespace std;
class Shape {protected:int width, height;public:Shape( int a=0, int b=0){count++; width = a;height = b;}int area(){cout << "Parent class area :" <<endl;return (width * height / 2);}static int count; //只能进行声明
};
int Shape::count=0;//查看总共生成的对象总数
int main( )
{cout<<"count="<<Shape::count<<endl;Shape s;Shape s1(2,3);cout<<s.area()<<endl;cout<<s1.area()<<endl;int *p=&Shape::count;
// int Shape::*p=&Shape::count; //这种做法将会报错([Error] cannot convert 'int*' to 'int Shape::*' in initialization),指针的转化的过程中 涉及到this,p指针不依赖于对象属于static的范围,所以要用普通指针来进行操作cout<< "count"<<*p<<endl; cout<<"count="<<Shape::count<<endl;return 0;
}
Java中的static
Java中的静态变量(类变量): static修饰, 主要用于修饰类成员方法, 加载优先于对象初始化(static成员在类被加载的时候就被加载了, 加载static成员会优先于), 静态代码块优先于构造方法执行, 被所有对象共享
- 整个类中只有一个值
- 类初始化的同时就被赋值
- 适用于所有类对象都具有的相同属性,经常需要共享的数据, 系统中的常量值
- 引用格式: 类名.变量名(或者静态函数名)
静态方法: 类方法,属于当前类, 调用直接类名.静态方法(), 只能是外部静态数据成员, 静态方法中不可出现this关键字; 静态方法中不能访问非静态成员(包含属性, this,super, 非静态方法); 原因在于当前对象未构造, 无属性,this,super存在, 但是反之即可(对象都创建了, 肯定能访问)
例:
public class test{public static void main(String[] args){System.out.println(A.show());A a1=new A();A a2=new A();A a3=new A();System.out.println(A.show());}
}
class A{public static int i=0;public A(){i++; System.out.println("构造方法");}public static int show(){return i;}
}
//结果将显示0 3,表示构造3个对象
静态方法调用非静态时引发的错误
public class test{public void info(){System.out.println("一般方法");}public static void main(String[] args){info();}
}
上述操作将出现错误静态上下文引用非静态变量, main()方法为static, 当main方法初始化的时候, 对象还未创建, 调用info的对象是this, this还未被创建,对上述操作有如下改进
public static void main(String[] args){new test().info();
}
注: 当涉及到super时, 效果与this一致;
java接口中不能有静态代码块(静态方法)
接口: 规定方法原型(方法名,参数列表,返回类型),不规定方法主体(静态代码块属于方法主体), 只具有方法的声明;
也包含基本数据类型的数据成员,但是都默认为static和final;
需要注意的是: java8中引入接口可以具有默认方法, 子类可以重写接口的方法
总结
C++与Java中的static用法具有区别, 但是核心都是static只属于类, 静态函数不能调用非静态变量(包含this,super, 一般成员函数,成员变量), 最主要的原因还是在于this的隐藏, 大多数的出现的静态调用非静态主要在于this,super以及在对象还未产生就调用对象的函数