程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 面向對象課後深入學習(C++ 類的靜態成員詳細講解),課後深入學習

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

編輯: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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved