面向对象课后深入学习(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