在一个类的定义中,如果使用了static关键字修饰成员,表明这些成员不是与类的实例绑定的。
语法:
static data_member (1)
static member_function (2)
解释说明:
一个类的静态成员不与该类的对象相关联,而是作为静态独立变量,或者常规函数。
还有,线程存储类的成员变量也是不与类的对象绑定的。
在类的定义中,使用static关键字只是用来声明这个静态变量,并没有定义,需要在类的外部定义这个静态成员,并不能加static否则编译错误:
class X
{
static int n; // declaration (uses 'static')
};
int X::n = 1; // definition (does not use 'static')
不管这个成员是私有还是公有,在外面定义或者同时初始化都没有问题。
类主体内的声明不是定义,所以可以将成员声明为不完整的类型(除void外),这样只要是已经声明过的类型就能使用。
class Foo;
class A
{
static int a[ ]; // declaration, incomplete type
static Foo x; // declaration, incomplete type
static A obj; // declaration, incomplete type (inside its own definition)
};
int A::a[10]; // definition, complete type
calss Foo { };
Foo A::x; // definition, complete type
A A::ojb; // // definition, complete type
为了引用类T的静态成员m,可以使用两种形式:限定名称T::m或成员访问表达式E.m或E->m,其中E是一个分别对T或T*进行评估的表达式。当在同一个类的范围内,限定名称是不需要的。
class X
{
public:
static void f(); // declaration
static int n; // declaration
}
X g() {return X();} // some function returning X
void f()
{
X::f(); // X::f is a qualified name of static member function,
// 使用类名限制符,表示命名空间,来访问类的静态成员。
g().f(); // g().f is member access expression referring to a static member function
// 使用类的实例对象来调用类的静态成员
}
int X::n = 7; // definition
void X::f() // definition
{
n = 1; // X::n is accessible as jst n in this scope, 直接访问不需修饰符。
}
类的静态成员遵守类成员访问规则(私有、保护、公共)。
类的静态成员函数
类的静态成员函数是和对象实例无关的,所以被调用时,是没有“this”指针的。
它也不能加以下修饰/限制符:virtual、const、volatile或者ref-qualified.
它的函数地址可以作为普通函数指针来使用,但不能作为类的成员函数指针来使用。
类的静态成员变量
同样的,静态数据成员不与任何对象相关联。即使没有定义该类的对象,它们也存在。在整个程序中只有一个静态数据成员的实例,具有静态存储期限,除非再加个关键字thread_local,在这种情况下,每个线程有一个这样的对象,具有线程存储期限(自C++11开始支持)。
静态数据成员不能声明为mutable类型。
在某个命名空间内,如果一个类是外部可链接的(external linkage),那这个类的静态成员也是外部可链接的。
如果这个命名空间是匿名的,那此命名空间的内容对外部就不是外部可链接的,而是内部链接(internal linkage),就是说只有当前编译单元(此源文件内部)内可链接。
局部定义的类(比如在函数内定义的类)和匿名类,包括这些类的成员类,不能包含静态成员变量,但可以拥有静态成员函数。
局部定义的类,只在局部作用域可访问其中的静态函数。
一个静态成员变量可以声明为inline,这样就在类声明时同时也定义并初始化,就不需要在class外面定义了。
class X
{
inline static in n = 1;
};
但这个特性时C++17才支持,所以需要在编译时加上参数:
$ g++ -o test test.cpp -std=c++17
常量静态成员变量
如果一个整型或枚举型的静态成员变量声明为const,可以直接在类的定义内部使用常量表达式来初始化。
class X
{
public:
const static int n = 1;
const static int m{2}; // since C++11, initializer list 功能。
const static int k;
};
const int X::k = 3; // 也可以在外部定义时初始化,const关键字是必须的。
如果类定义内部没有初始化,就需要在外部定义并初始化,并要加上const关键字,否则编译不过(static不能加,加了也编译不过)。
因为是const成员,所以初始化必须有且只能一次,要么类定义内部初始化,要么类定义外部初始化,否则就会编译不过。
如果使用constexpr定义的常量型静态成员变量,则必须要在类内部定义的时候,使用常量表达式初始化,表示要在编译期就确定常量变量的值,而不可以在动态执行期来确定。
class X
{
constexpr static int arr[] = {1, 2, 3};
constexpr static int k; // Error: constexpr static requires an initialize
};
在上面两种在类内部定义常量静态成员变量的情况中(使用const、constexpr),如果是odr-used,那么在此命名空间内还需要在类外部定义一下,可以不用初始化(类定义内部或外部初始化选其一)。
One Definition Rule (ODR),表示一个变量只能且至少一次定义。
下面的例子,因为要使用static变量的地址,所以需要在类外部进行定义一下,尽管是多余的定义。
class X
{
public:
static const int n = 1;
static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m;
const int X::n;
constexpr int X::m;
这里如果使用c++17标准编译的话,则constexpr可以不必在类外部定义。因为constexxpr是隐式的inline,不需要外部再声明。
所以就不建议在类外部再重新声明一下了。
test.cpp
class X
{
public:
constexpt static int n = 1;
};
const int *p = &X::n;
int main()
{
return 0;
}
$ g++ -o test test.cpp -std=c++17
编译OK,如果不指定c++17则编译失败。
-----------------------------------
关于static成员的访问控制:
1,static函数不能访问类的非static成员变量和非static成员函数。但普通成员函数可以访问静态成员变量或函数。
2,public static成员外部可以访问,使用子类也能访问。
3, protected static成员,子类的static函数和非static函数可以访问,但外部不能访问。
4, private static成员则只能本类内访问。
#include <stdio.h>
class A
{
public:
static int pubValue;
protected:
static int prtValue;
private:
static int prvValue;
public:
int funcA(){
return prvValue;
}
};
int A::pubValue=0;
int A::prtValue=0;
int A::prvValue=0;
class B: public A
{
public:
static void func(){
printf("%d %d\n", pubValue, prtValue);
}
int get_value()
{
return pubValue+prtValue;
}
};
int main()
{
int a = B::pubValue;
int b = B::prtValue; // Error
B::func();
B c;
c.get_value();
return 0;
}
参考:
https://en.cppreference.com/w/cpp/language/static
https://en.cppreference.com/w/cpp/language/static
本文详细介绍了C++中静态成员的概念及用法,包括静态成员变量和静态成员函数的特点,如何正确声明与定义静态成员,以及它们在类内外的访问方式。此外还探讨了不同访问级别下静态成员的可见性和使用限制。
4172





