C++之靜態(tài)函數(shù)用法及說(shuō)明
類(lèi)中的靜態(tài)函數(shù),就是屬于“類(lèi)本身”的函數(shù),不屬于某一個(gè)具體對(duì)象。
最典型的寫(xiě)法是:
class ZlogBase
{
public:
static void Initialize(const std::string& config_path);
static void Shutdown();
};
這兩個(gè)函數(shù)前面有 static,它們就是靜態(tài)成員函數(shù)。
一、怎么使用
靜態(tài)函數(shù)最常見(jiàn)的調(diào)用方式是:
ZlogBase::Initialize("log.conf");
ZlogBase::Shutdown();
也就是:
類(lèi)名::函數(shù)名()
因?yàn)樗鼘儆陬?lèi)本身,所以不需要先創(chuàng)建對(duì)象。
對(duì)比一下普通成員函數(shù)
普通成員函數(shù):
class ZlogBase
{
public:
void logInfo(const char* msg) const;
};
這種函數(shù)必須通過(guò)對(duì)象調(diào)用:
ZlogBase logger("tcpserver");
logger.logInfo("server start");
不能直接:
ZlogBase::logInfo("server start"); // 錯(cuò)
因?yàn)?logInfo() 需要依賴(lài)某個(gè)對(duì)象內(nèi)部的數(shù)據(jù),比如:
m_pZlogCategorythis
二、為什么靜態(tài)函數(shù)可以直接用類(lèi)名調(diào)用
因?yàn)殪o態(tài)函數(shù)沒(méi)有 this 指針。
普通成員函數(shù)在內(nèi)部其實(shí)默認(rèn)都帶著一個(gè)隱藏參數(shù):
this
表示“當(dāng)前對(duì)象是誰(shuí)”。
比如:
logger.logInfo("hello");
本質(zhì)上像是:
logger.logInfo(&logger, "hello");
只是這個(gè) this 是編譯器自動(dòng)傳進(jìn)去的。
但是靜態(tài)函數(shù)沒(méi)有 this,所以它不能訪(fǎng)問(wèn)對(duì)象成員變量,也不依賴(lài)某個(gè)對(duì)象。
既然它不依賴(lài)對(duì)象,那就可以直接:
ZlogBase::Initialize(...)
三、靜態(tài)函數(shù)里能做什么,不能做什么
能做的
可以做和“整個(gè)類(lèi)”有關(guān)、但和“某個(gè)具體對(duì)象”無(wú)關(guān)的事情。
例如:
- 全局初始化
- 全局釋放
- 計(jì)數(shù)器
- 工具函數(shù)
- 工廠(chǎng)函數(shù)
- 單例訪(fǎng)問(wèn)入口
不能直接做的
不能直接訪(fǎng)問(wèn)普通成員變量,比如:
class ZlogBase
{
protected:
zlog_category_t* m_pZlogCategory = nullptr;
public:
static void Initialize(const std::string& config_path);
};
在 Initialize() 里不能直接寫(xiě):
m_pZlogCategory = ...; // 錯(cuò)
因?yàn)?m_pZlogCategory 屬于某個(gè)對(duì)象,
而靜態(tài)函數(shù)不知道你想操作哪個(gè)對(duì)象。
四、為什么Initialize()適合做成靜態(tài)函數(shù)
你現(xiàn)在這個(gè)例子最典型:
ZlogBase::Initialize(LOG_FILE_PATH);
因?yàn)?ldquo;初始化日志系統(tǒng)”這件事,本質(zhì)上是整個(gè)程序級(jí)別的事情,不是某一個(gè) TcpServer 或某一個(gè) System 對(duì)象自己的事情。
它通常做的是:
zlog_init(LOG_FILE_PATH);
這是全局初始化。
所以它很適合寫(xiě)成:
static void Initialize(...);
這樣你在 main() 里程序啟動(dòng)時(shí)就能先調(diào)用:
ZlogBase::Initialize(LOG_FILE_PATH);
后面再去創(chuàng)建:
SystemTcpServerDevInfo
這些對(duì)象。
五、為什么要“先 Initialize,再創(chuàng)建對(duì)象”
因?yàn)楹芏鄬?duì)象在構(gòu)造時(shí)就要用日志。
比如:
class TcpServer : public ZlogBase
{
public:
TcpServer() : ZlogBase("tcpserver") {}
};
而 ZlogBase("tcpserver") 里面可能會(huì)調(diào)用:
zlog_get_category("tcpserver");
那前提就是:
zlog 系統(tǒng)已經(jīng)先初始化好了。
所以順序必須是:
ZlogBase::Initialize(...); // 先初始化整個(gè)日志系統(tǒng) TcpServer server; // 再創(chuàng)建具體對(duì)象
這就是為什么 Initialize() 設(shè)計(jì)成靜態(tài)函數(shù),并且在 main() 里直接調(diào)用最合理。
六、一個(gè)非常好理解的類(lèi)比
比如學(xué)校系統(tǒng):
class Student
{
public:
static void SetSchoolName(const std::string& name);
void SetStudentName(const std::string& name);
};
學(xué)校名稱(chēng)
這是所有學(xué)生共享的,屬于“整個(gè)類(lèi)”的事情,所以適合靜態(tài)函數(shù):
Student::SetSchoolName("No.1 Middle School");
學(xué)生姓名
這是某個(gè)具體學(xué)生自己的事情,所以必須通過(guò)對(duì)象:
Student s;
s.SetStudentName("Tom");
ZlogBase::Initialize()就像“設(shè)置學(xué)校名字”,是全局動(dòng)作。logInfo()就像“設(shè)置學(xué)生名字”,是對(duì)象動(dòng)作。
七、靜態(tài)函數(shù)常見(jiàn)使用場(chǎng)景
在實(shí)際項(xiàng)目里,靜態(tài)函數(shù)常見(jiàn)于這幾類(lèi):
1. 全局初始化 / 釋放
像你這個(gè):
ZlogBase::Initialize(...); ZlogBase::Shutdown();
2. 計(jì)數(shù)器
class Test
{
public:
static int Count();
};
統(tǒng)計(jì)一共創(chuàng)建了多少對(duì)象之類(lèi)。
3. 工具函數(shù)
class Math
{
public:
static int Add(int a, int b);
};
直接:
Math::Add(1, 2);
不需要先創(chuàng)建 Math 對(duì)象。
4. 單例入口
class Config
{
public:
static Config& instance();
};
八、靜態(tài)函數(shù)和靜態(tài)成員變量經(jīng)常配合使用
例如:
class ZlogBase
{
public:
static void Initialize(const std::string& path);
private:
static bool s_inited;
};
這里:
Initialize()是靜態(tài)函數(shù)s_inited是靜態(tài)變量
這兩個(gè)都屬于類(lèi)本身,而不是某個(gè)對(duì)象。
所以非常適合做“全局只初始化一次”的邏輯。
九、你現(xiàn)在這個(gè)例子應(yīng)該怎么理解
你截圖里的:
QCoreApplication a(argc, argv); ZlogBase::Initialize(LOG_FILE_PATH); System* pApp = new System(); pApp->Init();
應(yīng)該理解成:
第一步
程序啟動(dòng),先初始化日志系統(tǒng):
ZlogBase::Initialize(LOG_FILE_PATH);
第二步
再創(chuàng)建系統(tǒng)對(duì)象:
System* pApp = new System();
如果 System 繼承了 ZlogBase,那它在構(gòu)造時(shí)就能安全使用日志功能。
十、一句話(huà)總結(jié)
靜態(tài)函數(shù)用于處理“屬于整個(gè)類(lèi)、而不屬于某個(gè)具體對(duì)象”的事情。
所以像:
- 初始化日志系統(tǒng)
- 關(guān)閉日志系統(tǒng)
這種全局動(dòng)作,最適合寫(xiě)成:
ZlogBase::Initialize(...); ZlogBase::Shutdown();
而像:
logInfo()logError()
這種依賴(lài)對(duì)象內(nèi)部狀態(tài)的函數(shù),就必須通過(guò)對(duì)象調(diào)用。
如果你愿意,我下一條可以繼續(xù)專(zhuān)門(mén)講一下:
靜態(tài)成員變量和靜態(tài)成員函數(shù)的關(guān)系,以及它們?cè)?.h / .cpp 里應(yīng)該怎么寫(xiě)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C/C++表格組件Qt?TableWidget應(yīng)用詳解
本文詳細(xì)講解了C/C++中使用列表框組件Qt?TableWidget的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12
C++異步操作future和aysnc與function和bind
這篇文章主要介紹了C++異步操作future和aysnc與function和bind,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
C/C++中的atan和atan2函數(shù)實(shí)例用法
在本篇文章里小編給大家分享的是一篇關(guān)于C/C++中的atan和atan2函數(shù)實(shí)例用法相關(guān)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-02-02
QT實(shí)現(xiàn)簡(jiǎn)單時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了QT實(shí)現(xiàn)簡(jiǎn)單時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
詳解C++中new運(yùn)算符和delete運(yùn)算符的使用
這篇文章主要介紹了C++中new運(yùn)算符和delete運(yùn)算符的使用,文章來(lái)自于微軟開(kāi)發(fā)者文檔,因而根據(jù)Visual C++的一些特性來(lái)進(jìn)行講解,需要的朋友可以參考下2016-01-01
Windows下ncnn環(huán)境配置教程詳解(VS2019)
這篇文章主要介紹了Windows下ncnn環(huán)境配置(VS2019),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03

