回顾:静态成员变量的访问
在前一课《静态成员变量》中,我们学到:静态成员变量属于类本身,而非类的对象。若其访问权限为 public,可直接通过类名加作用域解析运算符 :: 访问:
#include <iostream>
class Something
{
public:
    static inline int s_value{ 1 };
};
int main()
{
    std::cout << Something::s_value; // s_value 为 public,可直接访问
}
若静态成员变量为 private,则无法直接访问:
#include <iostream>
class Something
{
private:                    // 现为 private
    static inline int s_value{ 1 };
};
int main()
{
    std::cout << Something::s_value; // 错误:s_value 为 private
}
此时,我们无法在 main() 中直接访问 Something::s_value。常规做法是通过公有的普通成员函数暴露接口,但这要求先实例化对象:
#include <iostream>
class Something
{
private:
    static inline int s_value{ 1 };
public:
    int getValue() { return s_value; } // 普通成员函数
};
int main()
{
    Something s{};
    std::cout << s.getValue(); // 可行,但必须实例化对象
}
显然,这种做法并不理想。
静态成员函数
成员函数亦可声明为 static。将上例改为静态成员函数:
#include <iostream>
class Something
{
private:
    static inline int s_value{ 1 };
public:
    static int getValue() { return s_value; } // 静态成员函数
};
int main()
{
    std::cout << Something::getValue() << '\n';
}
静态成员函数不与任何对象关联,可直接通过 类名:: 调用;也可通过对象调用,但不推荐。
静态成员函数的两个特点
- 无 - this指针
 静态成员函数不属于任何对象,自然没有- this指针。- 非静态成员必须依赖对象调用,因此需要 this;
- 静态成员函数无需对象即可执行,故无需 this。
 
- 非静态成员必须依赖对象调用,因此需要 
- 只能直接访问静态成员 
 静态成员函数可直接访问其他静态成员(变量或函数),但不能直接访问非静态成员,因为非静态成员必须依附于对象,而静态成员函数无对象可用。
在类外定义静态成员函数
静态成员函数亦可在类外实现,语法与普通成员函数一致:
#include <iostream>
class IDGenerator
{
private:
    static inline int s_nextID{ 1 };
public:
    static int getNextID(); // 声明
};
// 类外定义(注意无需再次使用 static)
int IDGenerator::getNextID() { return s_nextID++; }
int main()
{
    for (int count{ 0 }; count < 5; ++count)
        std::cout << "The next ID is: " << IDGenerator::getNextID() << '\n';
}
输出:
The next ID is: 1
The next ID is: 2
The next ID is: 3
The next ID is: 4
The next ID is: 5
由于所有数据与函数均为静态,无需实例化即可使用其功能。
头文件中的静态成员函数
根据《类与头文件》:
- 类内定义的静态成员函数隐式内联;
- 类外定义的静态成员函数不隐式内联,若置于头文件,应显式加 inline,以避免违反一次定义规则(ODR)。
全静态成员类的注意事项
编写“全静态成员类”(亦称“单态类”)时需留意:
- 单例性 
 所有静态成员仅实例化一次,无法拥有多个独立副本。如需两个独立的- IDGenerator,则无法用纯静态类实现。
- 全局状态风险 
 纯静态类等价于全局命名空间中的全局变量与函数,任何代码均可修改其状态,易导致难以察觉的错误。
 替代方案:- 使用普通类并实例化一个全局对象;
- 需要时仍可创建局部对象,保持灵活性。
 
纯静态类 vs 命名空间
- 相同点:均可在作用域内定义静态变量和函数。
- 差异点:类具备访问控制(private、protected、public),命名空间则无。
 若需隐藏数据成员或提供封装,优先使用类;否则使用命名空间。
C++ 无静态构造函数
C++ 不支持“静态构造函数”。若静态成员可直接初始化,可在定义处完成(即使为 private):
#include <iostream>
struct Chars
{
    char first{}, second{}, third{}, fourth{}, fifth{};
};
struct MyClass
{
    static inline Chars s_mychars{ 'a', 'e', 'i', 'o', 'u' };
};
int main()
{
    std::cout << MyClass::s_mychars.third; // 输出 i
}
若初始化需执行复杂逻辑(如循环),可借助函数返回对象:
#include <iostream>
struct Chars
{
    char first{}, second{}, third{}, fourth{}, fifth{};
};
class MyClass
{
private:
    static Chars generate()
    {
        Chars c{};
        c.first  = 'a';
        c.second = 'e';
        c.third  = 'i';
        c.fourth = 'o';
        c.fifth  = 'u';
        return c;
    }
public:
    static inline Chars s_mychars{ generate() };
};
int main()
{
    std::cout << MyClass::s_mychars.third; // 输出 i
}
相关技巧亦可用 lambda 实现,详见《全局随机数》。
小测验
问题 1
将以下 Random 命名空间改写为“全静态成员类”:
#include <chrono>
#include <random>
#include <iostream>
namespace Random
{
    inline std::mt19937 generate()
    {
        std::random_device rd{};
        std::seed_seq ss{
            static_cast<std::seed_seq::result_type>(
                std::chrono::steady_clock::now().time_since_epoch().count()),
            rd(), rd(), rd(), rd(), rd(), rd(), rd() };
        return std::mt19937{ ss };
    }
    inline std::mt19937 mt{ generate() };
    inline int get(int min, int max)
    {
        return std::uniform_int_distribution{min, max}(mt);
    }
}
int main()
{
    for (int count{ 1 }; count <= 10; ++count)
        std::cout << Random::get(1, 6) << '\t';
    std::cout << '\n';
    return 0;
}
(答案略)
