为什么引入static
声明
引入static
的声明的目的,是给linker提供一个提示,表明相应的变量或函数是“内部”的。
例如,在一个cpp文件中,static
声明的全局变量属于这个文件,不能被其他文件进行extern
。
同样的道理,在类内部声明的static变量和函数,表示这个变量或函数是类内部的,属于这个类,不能被类外部所使用。
在类中使用static
,是为了避免使用全局变量,污染整个命名空间(作用域限制在类内),同时,还可以使用全局特性(static
不在栈上创建)。
static
成员函数和成员变量
从这个角度来理解static
就能明白其表现出来的特性了。
1.对于static
成员函数来说:
1)static
声明的成员函数只能访问在同一个类内的static
变量,而不能访问非static
的(因为non-static的成员都是属于对象的,需要构造函数进行初始化的);
2)static
成员函数不能使用this
指针,因为this
指针指向的是具体的对象;
3)static
成员函数可以在类定义中定义,或者在类定义中声明后,在别处定义
(类中定义的成员函数视为inline,是固定的,在构造对象时,即使每个对象都有一个static
函数的副本,这些副本并没有什么不同,对外的表现仍是唯一定义,所以static成员函数在类定义时定义没有问题)。
2.对于static
成员变量来说(非const
):
1)只能在类定义中声明,不能进行初始化;
2)不能通过构造函数进行初始化,因为不属于任何对象;
3)必须在类定义的外部进行初始化
(static
变量如果内联到类定义中,每个对象中都有一个副本。然而,这个变量是可以被修改的,会造成同一类型的不同对象中存在不同的static
变量值,违反了static
变量具有全局特性的初衷)。
至于对static
成员变量和成员函数的访问,则可以通过类(用::操作符)或者对象(变量,指针,引用等)进行访问,对其的访问控制仍然受到访问控制符(public
,private
,protected
)的限制。
class Base{
public:
static int default_int; //Static变量仅仅能够在类定义中声明,不能初始化
static void change_int(int);//Static函数可以声明
static void change_char(char x){default_char=x;} //Static函数也可以定义
private:
static char default_char;
}
//在类外部定义static变量和static成员函数
int Base::default_int{1};
char Base::default_char{'a'};
void Base::change_int(int x){default_int=x;}
//访问static成员
using namespace std;
int main()
{
Base A{};
Base B{};
cout << "Access by object:" << A.default_int << endl
<< "Access by class:" << Base::default_int << endl;
cout << "Cannot access private:" << A.default_char << endl; //Error!访问控制符限制
A.change_int(10);//使用对象A来访问static方法,改变static变量的值
cout << "Changed static member:" << B.default_int << endl;
//static成员属于类的,通过另一个同类型对象访问这个static变量,其值改变
A.change_char('x');
}
使用const
声明
再说一些关于const
声明的问题:
1.const static
成员,使用了const
关键字表示这个成员变量是个常数,不会改变,因此必须在类定义中对其进行初始化,而不能在类定义外部。
这个限制其实跟static
没关系,本质上是const
声明的要求(类中定义的const
变量和引用必须进行初始化,而且是在类定义的时候进行,不能在对象构造期间进行,因此这两种类型的变量要么定义时初始化,要么通过in-class的构造函数初始化)。
但是对于const static
,要求更为严格,并不是所有类型都可以这样使用,只有int
,enum
和constexpr
才可以在类定义的内部进行初始化(属于类的,而且还要求是常量,通常只用于表示symbolic作用的变量)。
2.static
成员函数不能声明为const
的,因为const
声明表明函数不会改变类的成员变量,而static
成员函数根本不能访问类中的非static
成员,不符合const
的要求。
最后,关于static
成员变量的另一个用法,可以在类内声明自身类型的static
变量,而不能声明自身类型的非static
变量。这是因为非static
的变量需要构造函数进行初始化,而对自身类型的变量进行初始化又会调用构造函数,从而陷入无限循环。这样,在类定义中声明自身类型的变量,只能通过指针或者引用(指针在初始化时可以赋值nullptr
,引用一定会被初始化成另一个对象的引用,不会出错)。相反,使用static
声明表明这个变量是在其他地方定义的,不用关心其对应的对象是如何构造的。例如:
class Base{
public:
static Base A; //声明自身类型的成员变量,没问题
Base B; //Error!不能声明自身类型的非static成员变量
Base *C; //OK,C会被默认构造函数初始化成nullptr
}