C中的static使用比较简单,都不陌生了,C++中static关键字在类中的使用需要注意一些细节。static在类中修饰的是数据成员以及成员函数,分别称之为静态数据成员及静态成员函数。
先来看看static静态数据成员的目的及使用:
(1) 对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量。比如说统计某种类型对象已创建的数量。
(2) 如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量。这时我们可以用类的静态成员来解决这个问题。
(3) 非静态数据成员存在于类类型的每个对象中,静态数据成员则独立于该类的任意对象而存在,它是与类关联的对象,不与类对象关联。
static静态数据成员的优点:
(1) static静态数据成员的名字是在类的作用域中,因此可以避免与其它类成员或全局对象名字冲突。
(2) 可以实施封装,static静态数据成员可以是私有的,而全局对象则不可以。
(3) 阅读程序容易看出static成员与某个类相关联,这种可见性可以清晰地反映程序员的意图。
static成员函数需要注意的事项:
(1) static成员函数没有this指针,因为它是与类关联的,不与对象关联
(2) 非静态成员函数可以访问静态成员
(3) 静态成员函数不可以访问非静态成员。
下面举几个例子来说明静态数据成员及静态成员函数的使用方法。
先来看看下面的统计对象个数的类的定义。
CountedObject.h
#ifndef _COUNTED_OBJECT_H #define _COUNTED_OBJECT_H class CountedObject { public: CountedObject(); ~CountedObject(); public: static int GetCount(); //静态成员函数 private: static int count_; // 静态成员函数在定义时有两步:1 静态成员的引用性声明 2 在文件域上进行定义性说明。 这里只是第一步 }; #endif
#include "CountedObject.h" int CountedObject::count_ = 100; //静态成员函数定义性声明方法:1 需要加上类名域运算符 2不需要static关键字 3 只能在文件作用域中进行初始化,不能在类中进行初始化 CountedObject::CountedObject() { ++count_; //创建对象时自加 } CountedObject::~CountedObject() { --count_; 销毁对象时自减 } //静态成员函数在定义时不加static修饰,只能访问静态数据成员,不能访问普通数据成员 int CountedObject::GetCount() { return count_; }
Test1.cpp
#include "CountedObject.h" #include <iostream> using namespace std; int main() { //cout << CountedObject::count_ << endl; //静态数据成员可以用类名来访问,如果是私有的则不能访问了,只能提供一个公有接口来访问 cout << CountedObject::GetCount() << endl; //output:100 CountedObject co1; cout << CountedObject::GetCount() << endl; //output:101 CountedObject *co2 = new CountedObject; cout << CountedObject::GetCount() << endl; //output:102 delete co2; cout << CountedObject::GetCount() << endl; //output:101 return 0; }
静态常量数据成员
首先,静态常量数据成员它属于静态的数据成员,与静态数据成员不同的是,它的值是const,不能被修改。并且静态常量数据成员也是与类关联的,全体所有对象共享。普通成员函数及静态成员函数均可以访问它。
#include <iostream> using namespace std; class Test { public: Test(){} ~Test(){} public: //static const int x_; //1 与static int x_;不同的是,它的初始化可以在类体中进行 //2 但是也只有static const int, static const char(静态常量整型数据成员)才可以在类体中初始化,如double则不行. //3 但是静态常量整型数据成员在类体中初始化方法在VC6.0则是行不通的,VC6.0编译器比较古老,不支持。 //所以个人还是觉得统一一下,在类体外初始化比较好。 static const int x_ = 100; //静态常量数据成员 }; //const int Test::x_ = 100; const int Test::x_; int main(void) { Test t; cout << t.x_ << endl; //output:100 cout << Test::x_ << endl; //output:100 //Test::x_ = Test::x_ + 1; //Error,不能给常量赋值 //t.x_ = t.x_ + 1; //同上 return 0; }
下面的例子是关于普通成员函数可以访问静态数据成员及静态成员函数,而静态成员函数只能访问静态数据成员,而不能访问普通数据成员的例子(原因是静态成员函数没有this指针)
#include <iostream> using namespace std; class Test { public: Test(int y):y_(y) { } ~Test(){} public: void TestFun() { cout << "x="<< x_ << endl; //非静态成员函数访问静态数据成员 TestStaticFun(); //非静态成员函数访问静态成员函数 } static void TestStaticFun() { //TestFun(); //Error,静态成员函数不能访问非静态成员函数 //cout << "y = "<< y_ << endl; //Error,静态成员函数不能访问非静态成员,因为静态成员函数没有隐含的this指针,故无法指向普通对象的非静态成员。而且也不能访问非静态成员函数,因为非静态成员与非静态成员函数是属于对象的,而静态成员与静态成员函数是属于类的,但是对象可以访问类的静态成员与静态成员函数 cout << "TestStaticFun()..x="<<x_<<endl; } static int x_; //静态成员的引用性声明 int y_; }; int Test::x_ = 100; //静态数据成员的定义性说明。 int main() { Test t(10); t.TestFun(); cout << t.x_ << endl; //对象可以访问静态成员。这是允许的,但是不推荐,让人误解以为x_是属于t对象的。 cout << Test::x_ << endl;//推荐这么访问 return 0; }
关于类/对象大小的计算
(1) 首先,类大小的计算遵循结构体的对齐原则
(2) 类的大小与普通数据成员有关,与成员函数无关,即普通成员函数,静态成员函数,静态数据成员,静态常量数据成员均对类的大小无影响
(3) 虚函数对类的大小有影响(后面再说明),主要是虚表指针带来的影响
(4) 虚继承对类的大小有影响(后面再说明),主要是虚基表指针带来的影响
按照上面的类大小计算原则,对前面一个例子的Test类大小可以进行计算,由于它只有一个普通数据成员y_,静态数据成员与静态成员函数,普通成员函数均对类大小无影响,所以前一个例子的类大小为:4bytes,如果不信可以用sizeof(Test)进行计算测试。^_^