在C++的类中使用static申明

Last Updated: 2023-12-25 07:47:28 Monday

-- TOC --

在C++的类定义中(class/struct),可以存在对成员变量或函数的static申明,它们被称为static member,其含义与C中的static大同小异。

C++把那些从C开始就有的所谓的fundamental types也看做object,另一个runtime概念是object life cycle。C语言中的全局变量,局部变量,static变量,统统都放在object life cycle这个范畴下,在C++中进行重新的理解,其实就是这些变量的memory什么时候申请,什么时候释放。

static表示对象的storage不是动态的,而是静态的,即在程序开始时allocate,在程序退出时才deallocate。static申明出现的位置,决定了其可被访问的scope。

class中的static变量

static变量不与对象绑定,运行时只有一个副本,存放在.data区域。

Static members are members of a class that aren’t associated with a particular instance of the class. Normal class members have lifetimes nested within the class’s lifetime, but static members have static storage duration. These members are essentially similar to static variables and functions declared at global scope; however, you must refer to them by the containing class’s name, using the scope resolution operator ::. In fact, you must initialize static members at global scope. You cannot initialize a static member within a containing class definition.

普通成员变量因为多个类对象的实例化,会存在多个不同的副本,其生命周期与对象生命周期相同。而static变量不与对象绑定,在没有将类实例化的时候,它也存在那里,只是不一定能够被代码访问。运行时不管实例化了多少个对象,static成员变量都只有一份,它一直在那里,静静地待着。

类中的静态成员变量必须在类中声明,在类外定义(被const修饰的除外),这似乎是ISO C++的一个硬性规定,如果在申明时直接赋初值,会看到如下错误提示:

error: ISO C++ forbids in-class initialization of non-const static member

static变量必须在class外面做初始化,否则后面的代码无法引用它:

#include <iostream>
using namespace std;

struct xyz {
    int a;
    int b;
    static int c;
    int d;
    xyz(int a, int b, int d): a{a},b{b},d{d} {}
};

int xyz::c {3};

int main(void){
    xyz x{1,2,4};
    cout << x.a << x.b << xyz::c << x.d << endl;
    return 0;
}

输出:1234

注意:

  1. 初始化列表(initialization list)有个初始化顺序要求,只是顺序,可以跳过某些变量,包括static member;
  2. 必须要在class/struct外面再次申明static member,是否有初始值,可选。即上面的那行代码,可以写成这样int xyz::c;,不初始化,运行起始其值必为0。

还记得C语言中关于变量初始化的知识吗?

编译器会保证没有初始化的全局变量和静态变量,在程序加载的时候,全部初始化为0,并且,这些没有初始化的全局和静态变量,不占用.data section的空间,只是在bss段有个记录,这可以有效减小程序文件的size。

一般定义都在头文件中,如果被多个代码文件引用,static成员如果赋初值,就是重复定义了。这种被多个代码文件引用的情况,就必须要选择一个代码文件,来给static成员做初始化。如下代码示例:

// a header file:
struct OtherType {
    static int classCounter;
    // ...
};

// implementation, cpp file
int OtherType::classCounter = 0;

C++17开始,优化了这个细节,使用inline static member,直接在定义时初始化,无需在代码文件中做这个事情了,这样C++代码优雅了不少!示例代码如下:

// a header file, C++17:
struct OtherType {
    inline static int classCounter = 0;
    // ...
};

而const static member,也只能做in-class初始化:(也许应该使用constexpr,编译期也可以用)

#include <iostream>
using namespace std;

struct xyz {
    int a;
    int b;
    const static int c {3};
    int d;
    xyz(int a, int b, int d): a{a},b{b},d{d} {}
};

//int xyz::c{3}; // not needed anymore!!

int main(void){
    xyz x{1,2,4};
    cout << x.a << x.b << xyz::c << x.d << endl;
    return 0;
}

还需要注意static member的可访问范围,如果不是public,只有成员函数可以访问。

#include <iostream>
using namespace std;

class xyz {
    protected:
        static int a;
    public:
        static void set_a(int b);
        void show_a(void);
};

int xyz::a = 1;

void xyz::set_a(int b) {
    a = b;  // a is the static one
}

void xyz::show_a(void) {
    cout << a << endl;
}

int main(void) {
    // cout << xyz::a << endl; // wrong, xyz::a is protected!!
    xyz x;
    x.show_a();
    xyz::set_a(10);  // right
    x.show_a();
    return 0;
}

C++ class中的static成员变量,类似python的class variable

class中的static成员函数

上面的例子展示了一个static成员函数的case。

static成员函数同样不与对象绑定,对它的调用,虽然可以通过对象,但推荐使用class::static_function的方式。静态成员函数仅能访问静态数据成员或其他静态成员函数,它们无法访问类的非静态数据成员或成员函数,因为他们没有this指针

定义在成员函数中的static变量

不同对象的这个拥有static变量的成员函数,共享这个static变量,在多线程场景下,如果不加保护,这种用法恐怕会有问题:

#include <iostream>
using namespace std;

struct bob{
    int a;
    bob(int a): a{a} {}
    void show(void){
        static int b = 11;
        cout << a << " " << b++ << endl;
    }
};

int main(void) {
    bob b1{1};
    b1.show();  // 1 11
    bob b2{2};
    b2.show();  // 2 12
    bob b3{3};
    b3.show();  // 3 13
    return 0;
}

static class variable是所有对象的所有接口共享。static function variable是所有对象的那个具体的function共享。

static成员变量的继承

如下测试代码:

$ cat test_static.cpp
#include <iostream>
using namespace std;


class xyz {
    protected:
        static int a;
    public:
        static void set_a(int b);
        void show_a(void);
};


int xyz::a = 1;  // must be defined outside class xyz


void xyz::set_a(int b) {
    a = b;  // a is the static one
}


void xyz::show_a(void) {
    cout << a << endl;
}


class x2: public xyz {};


int main(void) {
    // cout << xyz::a << endl; // wrong reference of xyz::a
    x2 x;
    x.show_a();
    x2::set_a(10);  // right
    x.show_a();
    xyz y;
    y.show_a();
    xyz::set_a(99);
    x.show_a();
    return 0;
}

输出:

$ g++ test_static.cpp
$ ./a.out
1
10
10
99

static成员变量,只有一个副本,继承后,不管是父类对象还是子类对象,指向相同。

static class

Python中的@staticmethod

python中的@staticmethod装饰器

本文链接:https://cs.pynote.net/sf/c/cpp/202207211/

-- EOF --

-- MORE --