面向對象課後深入學習(C++ 類的靜態成員詳細講解)

来源:http://www.cnblogs.com/kpxy/archive/2016/06/21/5604897.html
-Advertisement-
Play Games

今天在剛申請了博客,一下午都在寫那個隨筆,所以說好的來看c++的也放在了最後,下星期就考試了,這個類的靜態成員是我不是很懂的,在網上 看到一片很詳細的博客考下來回去慢慢看。 在C++中,靜態成員是屬於整個類的而不是某個對象,靜態成員變數只存儲一份供所有對象共用。所以在所有對象中都可以共用它。使用靜態 ...


今天在剛申請了博客,一下午都在寫那個隨筆,所以說好的來看c++的也放在了最後,下星期就考試了,這個類的靜態成員是我不是很懂的,在網上 看到一片很詳細的博客考下來回去慢慢看。

在C++中,靜態成員是屬於整個類的而不是某個對象,靜態成員變數只存儲一份供所有對象共用。所以在所有對象中都可以共用它。使用靜態成員變數實現多個對象之間的數據共用不會破壞隱藏的原則,保證了安全性還可以節省記憶體。

靜態成員的定義或聲明要加個關鍵static。靜態成員可以通過雙冒號來使用即<類名>::<靜態成員名>。

 

在C++中類的靜態成員變數和靜態成員函數是個容易出錯的地方,本文先通過幾個例子來總結靜態成員變數和成員函數使用規則,再給出一個實例來加深印象。希望閱讀本文可以使讀者對類的靜態成員變數和成員函數有更為深刻的認識。

 

第一個例子,通過類名調用靜態成員函數和非靜態成員函數

 1 class Point  
 2 {  
 3 public:   
 4     void init()  
 5     {    
 6     }  
 7     static void output()  
 8     {  
 9     }  
10 };  
11 void main()  
12 {  
13     Point::init();  
14     Point::output();  
15 }  

編譯出錯:error C2352: 'Point::init' : illegal call of non-static member function

結論1:不能通過類名來調用類的非靜態成員函數。

 

第二個例子,通過類的對象調用靜態成員函數和非靜態成員函數

將上例的main()改為:

1 void main()  
2 {  
3     Point pt;  
4     pt.init();  
5     pt.output();  
6 }  

編譯通過。

結論2:類的對象可以使用靜態成員函數和非靜態成員函數。

 

第三個例子,在類的靜態成員函數中使用類的非靜態成員

 1 #include <stdio.h>  
 2 class Point  
 3 {  
 4 public:   
 5     void init()  
 6     {    
 7     }  
 8     static void output()  
 9     {  
10         printf("%d\n", m_x);  
11     }  
12 private:  
13     int m_x;  
14 };  
15 void main()  
16 {  
17     Point pt;  
18     pt.output();  
19 }  

編譯出錯:error C2597: illegal reference to data member 'Point::m_x' in a static member function

因為靜態成員函數屬於整個類,在類實例化對象之前就已經分配空間了,而類的非靜態成員必須在類實例化對象後才有記憶體空間,所以這個調用就出錯了,就好比沒有聲明一個變數卻提前使用它一樣。

結論3:靜態成員函數中不能引用非靜態成員。

 

第四個例子,在類的非靜態成員函數中使用類的靜態成員

 1 class Point  
 2 {  
 3 public:   
 4     void init()  
 5     {    
 6         output();  
 7     }  
 8     static void output()  
 9     {  
10     }  
11 };  
12 void main()  
13 {  
14     Point pt;  
15     pt.output();  
16 }  

編譯通過。

結論4:類的非靜態成員函數可以調用用靜態成員函數,但反之不能。

 

第五個例子,使用類的靜態成員變數

 1 #include <stdio.h>  
 2 class Point  
 3 {  
 4 public:   
 5     Point()  
 6     {    
 7         m_nPointCount++;  
 8     }  
 9     ~Point()  
10     {  
11         m_nPointCount--;  
12     }  
13     static void output()  
14     {  
15         printf("%d\n", m_nPointCount);  
16     }  
17 private:  
18     static int m_nPointCount;  
19 };  
20 void main()  
21 {  
22     Point pt;  
23     pt.output();  
24 }  

按Ctrl+F7編譯無錯誤,按F7生成EXE程式時報鏈接錯誤

error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)

這是因為類的靜態成員變數在使用前必須先初始化。

在main()函數前加上int Point::m_nPointCount = 0;

再編譯鏈接無錯誤,運行程式將輸出1。

結論5:類的靜態成員變數必須先初始化再使用。

 

結合上面的五個例子,對類的靜態成員變數和成員函數作個總結:

一。靜態成員函數中不能調用非靜態成員。

二。非靜態成員函數中可以調用靜態成員。因為靜態成員屬於類本身,在類的對象產生之前就已經存在了,所以在非靜態成員函數中是可以調用靜態成員的。

三。靜態成員變數使用前必須先初始化(如int MyClass::m_nNumber = 0;),否則會在linker時出錯。

 

再給一個利用類的靜態成員變數和函數的例子以加深理解,這個例子建立一個學生類,每個學生類的對象將組成一個雙向鏈表,用一個靜態成員變數記錄這個雙向鏈表的表頭,一個靜態成員函數輸出這個雙向鏈表。

 1 #include <stdio.h>  
 2 #include <string.h>  
 3 const int MAX_NAME_SIZE = 30;    
 4   
 5 class Student    
 6 {    
 7 public:    
 8     Student(char *pszName);  
 9     ~Student();  
10 public:  
11     static void PrintfAllStudents();  
12 private:    
13     char    m_name[MAX_NAME_SIZE];    
14     Student *next;  
15     Student *prev;  
16     static Student *m_head;  
17 };    
18   
19 Student::Student(char *pszName)  
20 {    
21     strcpy(this->m_name, pszName);  
22   
23     //建立雙向鏈表,新數據從鏈表頭部插入。  
24     this->next = m_head;  
25     this->prev = NULL;  
26     if (m_head != NULL)  
27         m_head->prev = this;  
28     m_head = this;    
29 }    
30   
31 Student::~Student ()//析構過程就是節點的脫離過程    
32 {    
33     if (this == m_head) //該節點就是頭節點。  
34     {  
35         m_head = this->next;  
36     }  
37     else  
38     {  
39         this->prev->next = this->next;  
40         this->next->prev = this->prev;  
41     }  
42 }    
43   
44 void Student::PrintfAllStudents()  
45 {  
46     for (Student *p = m_head; p != NULL; p = p->next)  
47         printf("%s\n", p->m_name);  
48 }  
49   
50 Student* Student::m_head = NULL;    
51   
52 void main()    
53 {     
54     Student studentA("AAA");  
55     Student studentB("BBB");  
56     Student studentC("CCC");  
57     Student studentD("DDD");  
58     Student student("MoreWindows");  
59     Student::PrintfAllStudents();  
60 }  

程式將輸出:

轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/6721430

 

 

 

 

仔細看過以後感覺樓主寫的非常好,對我有很多啟發。哈哈
除此之外 ,我感覺還有個小瑕疵,就是析構函數還可以再嚴謹些。 

 1 Student::~Student ()//析構過程就是節點的脫離過程    
 2 {     
 3     if (this == m_head) //該節點就是頭節點。  
 4     {  
 5         Student *ptmp = m_head;  
 6         m_head = this->next;  
 7         cout << "in destructor head " << endl;    
 8     }  
 9                 else if(this->next == NULL)     
10                 { this->pre->next =NULL ;}  
11                 else {     
12         this->prev->next = this->next;  
13         this->next->prev = this->prev;  
14     }  
15 }   

我增加了 
else if(this->next == NULL) 
{ this->pre->next =NULL ;}
否則 this->next->prev 將有問題,因為 this->next 都NULL了 還怎麼能找prev域呢? 
測試: new *p1 = ...
new *p2 = ...
new *p3 = ...
//單獨 delete p3 is fine
//單獨 delete p2 is fine
單獨 but delete p1 is wrong //這是最後一個,因為是前插
修改後的析構函數沒有這類問題了。均能正常調用析構函數析構。

2016-06-21

20:13:22


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例設計模式 Singleton是一種創建型模式,指某個類採用Singleton模式,則在這個類被創建後,只可能產生一個實例供外部訪問,並且提供一個全局的訪問點。 核心知識點如下: (1) 將採用單例設計模式的類的構造方法私有化(採用private修飾)。 (2) 在其內部產生該類的實例化對象,並將 ...
  • 關於scala的類型推斷前面已經提到過多次。再來看一下下麵這個例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var list2 = new ArrayList[Int] list2 add 1 list2 add 2... ...
  • 今天(周一)看到一位同仁的生活規劃,感覺挺適合我,實踐一下,就theo&tools day+code day+blog day,間歇性有happy day嘛~ blog day這樣做,一篇blog總結,內容多的分出去寫單篇,在總結篇中給鏈接 2016.06.20 本來打算寫一個小網頁的,關於摩斯碼的 ...
  • 1:readline() 一行一行得從文件讀數據,顯然比較慢; 不過很省記憶體; 測試讀10M的sample.txt文件,每秒大約讀32000行; 2:fileinput 寫法簡單一些,不過測試以後發現每秒只能讀13000行數據,效率比上一種方法慢了兩倍多; 3:readlines() 用同樣的數據測 ...
  • 程式運行為什麼需要記憶體(一) 1、程式的目的是運行得到一定的結果。 2、為了得到不同的結果,就要運行不同的程式。 3、電腦程式的本質都是在做計算,計算就是在計算數據,所以電腦程式中重要的部分就是數據。 電腦程式 = 代碼 + 數據 4、從巨集觀上理解代碼就是動作,即加工數據的動作。數據就是數字, ...
  • 考試科目: 添加考試科目,填寫科目名稱,選擇科目題型(覆選框/單選題,多選題,判斷題,問答題,填空題) 添加科目章節,填寫章節名稱,添加章節知識點,填寫知識點以英文逗號分隔,直接插入多條記錄 開通考場: 填寫考場名稱,選擇考場狀態(開啟/關閉),選擇考試科目(下拉框) 考試試題: 添加試題,選擇題型 ...
  • 本章內容: Socket IO多路復用(select) SocketServer 模塊(ThreadingTCPServer源碼剖析) socket通常也稱作"套接字",用於描述IP地址和埠,是一個通信鏈的句柄,應用程式通常通過"套接字"向網路發出請求或者應答網路請求。 功能: sk = sock ...
  • 恢復內容開始 最近嘗試在MAC(OS X 10.11 El Capitan)上安裝Caffe 以及Python介面遇到了一些問題但是官方安裝教程上並沒有提出這些問題的解決辦法搜索了很久(主要在於Python介面上) 終於找到瞭解決辦法 其實Caffe的安裝分兩步:安裝依賴+編譯源碼 首先是安裝依賴: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...