C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++之静态函数

C++之静态函数用法及说明

作者:IOT-Power

title:理解静态函数的使用场景和规则description:本文详细解释了静态函数的概念及其使用场景,包括全局初始化、释放资源和工具函数等-3个对象,重点在于于处理整个类而非单个对象的操作,通过具体例子帮助读者更好地理解和应用,静的成员函数的使用场景和编写方式

类中的静态函数,就是属于“类本身”的函数,不属于某一个具体对象。

最典型的写法是:

class ZlogBase
{
public:
    static void Initialize(const std::string& config_path);
    static void Shutdown();
};

这两个函数前面有 static,它们就是静态成员函数。

一、怎么使用

静态函数最常见的调用方式是:

ZlogBase::Initialize("log.conf");
ZlogBase::Shutdown();

也就是:

类名::函数名()

因为它属于类本身,所以不需要先创建对象。

对比一下普通成员函数

普通成员函数:

class ZlogBase
{
public:
    void logInfo(const char* msg) const;
};

这种函数必须通过对象调用:

ZlogBase logger("tcpserver");
logger.logInfo("server start");

不能直接:

ZlogBase::logInfo("server start");   // 错

因为 logInfo() 需要依赖某个对象内部的数据,比如:

二、为什么静态函数可以直接用类名调用

因为静态函数没有 this 指针

普通成员函数在内部其实默认都带着一个隐藏参数:

this

表示“当前对象是谁”。

比如:

logger.logInfo("hello");

本质上像是:

logger.logInfo(&logger, "hello");

只是这个 this 是编译器自动传进去的。

但是静态函数没有 this,所以它不能访问对象成员变量,也不依赖某个对象。
既然它不依赖对象,那就可以直接:

ZlogBase::Initialize(...)

三、静态函数里能做什么,不能做什么

能做的

可以做和“整个类”有关、但和“某个具体对象”无关的事情。

例如:

不能直接做的

不能直接访问普通成员变量,比如:

class ZlogBase
{
protected:
    zlog_category_t* m_pZlogCategory = nullptr;

public:
    static void Initialize(const std::string& config_path);
};

Initialize() 里不能直接写:

m_pZlogCategory = ...;   // 错

因为 m_pZlogCategory 属于某个对象,
而静态函数不知道你想操作哪个对象。

四、为什么Initialize()适合做成静态函数

你现在这个例子最典型:

ZlogBase::Initialize(LOG_FILE_PATH);

因为“初始化日志系统”这件事,本质上是整个程序级别的事情,不是某一个 TcpServer 或某一个 System 对象自己的事情。

它通常做的是:

zlog_init(LOG_FILE_PATH);

这是全局初始化。

所以它很适合写成:

static void Initialize(...);

这样你在 main() 里程序启动时就能先调用:

ZlogBase::Initialize(LOG_FILE_PATH);

后面再去创建:

这些对象。

五、为什么要“先 Initialize,再创建对象”

因为很多对象在构造时就要用日志。

比如:

class TcpServer : public ZlogBase
{
public:
    TcpServer() : ZlogBase("tcpserver") {}
};

ZlogBase("tcpserver") 里面可能会调用:

zlog_get_category("tcpserver");

那前提就是:

zlog 系统已经先初始化好了。

所以顺序必须是:

ZlogBase::Initialize(...);   // 先初始化整个日志系统
TcpServer server;            // 再创建具体对象

这就是为什么 Initialize() 设计成静态函数,并且在 main() 里直接调用最合理。

六、一个非常好理解的类比

比如学校系统:

class Student
{
public:
    static void SetSchoolName(const std::string& name);
    void SetStudentName(const std::string& name);
};

学校名称

这是所有学生共享的,属于“整个类”的事情,所以适合静态函数:

Student::SetSchoolName("No.1 Middle School");

学生姓名

这是某个具体学生自己的事情,所以必须通过对象:

Student s;
s.SetStudentName("Tom");

七、静态函数常见使用场景

在实际项目里,静态函数常见于这几类:

1. 全局初始化 / 释放

像你这个:

ZlogBase::Initialize(...);
ZlogBase::Shutdown();

2. 计数器

class Test
{
public:
    static int Count();
};

统计一共创建了多少对象之类。

3. 工具函数

class Math
{
public:
    static int Add(int a, int b);
};

直接:

Math::Add(1, 2);

不需要先创建 Math 对象。

4. 单例入口

class Config
{
public:
    static Config& instance();
};

八、静态函数和静态成员变量经常配合使用

例如:

class ZlogBase
{
public:
    static void Initialize(const std::string& path);

private:
    static bool s_inited;
};

这里:

这两个都属于类本身,而不是某个对象。

所以非常适合做“全局只初始化一次”的逻辑。

九、你现在这个例子应该怎么理解

你截图里的:

QCoreApplication a(argc, argv);
ZlogBase::Initialize(LOG_FILE_PATH);

System* pApp = new System();
pApp->Init();

应该理解成:

第一步

程序启动,先初始化日志系统:

ZlogBase::Initialize(LOG_FILE_PATH);

第二步

再创建系统对象:

System* pApp = new System();

如果 System 继承了 ZlogBase,那它在构造时就能安全使用日志功能。

十、一句话总结

静态函数用于处理“属于整个类、而不属于某个具体对象”的事情。

所以像:

这种全局动作,最适合写成:

ZlogBase::Initialize(...);
ZlogBase::Shutdown();

而像:

这种依赖对象内部状态的函数,就必须通过对象调用。

如果你愿意,我下一条可以继续专门讲一下:

静态成员变量和静态成员函数的关系,以及它们在 .h / .cpp 里应该怎么写。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文