1. static概念
声明 为static的类成员称为类的静态成员,用static修饰的成员变量,称为静态成员变量;用static修饰的成员函数,称为静态成员函数,都存放在堆区。
静态成员变量一定要在类外进行初始化。
为什么静态成员变量一定要在类外进行初始化呢?
在类中,仅仅是声明了静态变量(告诉我们有这个成员变量),并没有定义(定义需要分配内存)。
声明只是表明了变量的数据类型和属性,并不分配内存;定义则需要分配内存。 注意:如果在类里面这么写 int a ;那么既声明了变量,也定义了变量,两者合在一起了。
静态成员是“类级别的”,它和类的地位等同,而普通成员是“对象(实例)级别的”。类级别的成员,先于该类的任何对象而存在,是属于所有对象的,被该类的所有对象共享。
现在,咱们假定要实例化该类的一个对象,那么会发生什么事情呢?
静态成员肯定要出现在这个对象里面的,对吧?这时候才去定义那个静态成员吗?显然不合适!因为,比如有另外一个线程也要创建该类的对象,那么也要按照这个方式去定义那个静态成员。
这可能会产生两种情况:
A. 重复定义;
B. 就算不产生重复定义的情况,也会产生竞争,造成死锁。
显然编译器不能这么干。很合理的解决办法,就是在类的外部事先把它定义好,然后再供所有的对象共享。
class A { public: static int _a; static int _b; }; int A::_a = 10; // 定义静态成员变量,同时也初始化。 int A::_b; // 定义静态成员变量,不初始化也可以。 int main() { A T1; cout << T1._a << endl; // 10 cout << A:: _b << endl; // 0 return 0; }
静态成员函数既可以在类内定义,也可以在类外定义,类外定义不需要加static。
普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。
编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。
普通成员变量占用对象的内存,静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,也就是说静态成员函数不能访问普通成员变量,只能访问静态成员变量。
普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。
class A { public: A() { ++_count; } A(const A& t) { ++_count; } ~A() { --_count; } static int GetACount() // 类内定义,只能访问静态成员 { return _count; } private: static int _count; }; int A::_count = 0; void TestA() { cout << A::GetACount() << endl; // 可以直接调用类 A a1, a2; A a3(a1); cout << a3.GetACount() << endl; // 也可以直接使用对象调用 } int main() { TestA(); return 0; }
总结:
静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
2. 使用细节
特性:
- 静态成员为所有类对象共享,存放在静态区(在对象实例化之前就已经定义了)。
- 静态成员变量必须在类外定义,定义时不添加static关键字。
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制。
3. 易错点
问题1:
静态成员函数可以调用非静态成员函数吗?
不可以,因为静态成员函数没有this指针,普通成员函数必须有相应的对象地址(this)才能调用,因为普通函数里面要用this来访问对象。
问题2:
非静态成员函数可以调用类的静态成员函数吗?
可以,静态成员函数属于整个类,既可以使用类域(A::)进行调用,也可以使用对象(a1.)进行调用。