C++实操 - 类的static成员

本文详细介绍了C++中静态成员的概念及用法,包括静态成员变量和静态成员函数的特点,如何正确声明与定义静态成员,以及它们在类内外的访问方式。此外还探讨了不同访问级别下静态成员的可见性和使用限制。

在一个类的定义中,如果使用了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/statichttps://en.cppreference.com/w/cpp/language/static

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值