目录
1. 简介
● 在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。
● 静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。
●静态成员的定义或声明要加个关键static。静态成员可以通过双冒号来使用即<类名>::<静态成员名>。
2. 实例
在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象。希望阅读本文可以使读者对类的静态成员变量和成员函数有更为深刻的认识。
2.1 通过类名调用"静态成员函数"和"非静态成员函数"
class Point
{
public:
void init()
{
}
static void output()
{
}
};
void main()
{
Point::init();
Point::output();
}
编译出错:error C2352: ‘Point::init’ : illegal call of non-static member function
结论1:不能通过类名来调用类的非静态成员函数。
2.2 通过类的对象调用"静态成员函数"和"非静态成员函数"
将上例的main()改为:
void main()
{
Point pt;
pt.init();
pt.output();
}
编译通过。
结论2:类的对象可以使用静态成员函数和非静态成员函数。
2.3 在类的"静态成员函数"中使用类的"非静态成员"
#include <stdio.h>
class Point
{
public:
void init()
{
}
static void output()
{
printf("%d\n", m_x);
}
private:
int m_x;
};
void main()
{
Point pt;
pt.output();
}
编译出错:error C2597: illegal reference to data member ‘Point::m_x’ in a static member function
因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了,就好比没有声明一个变量却提前使用它一样。
结论3:静态成员函数中不能引用非静态成员。
2.4 在类的非静态成员函数中使用类的静态成员
class Point
{
public:
void init()
{
output();
}
static void output()
{
}
};
void main()
{
Point pt;
pt.output();
}
编译通过。
结论4:类的非静态成员函数可以调用用静态成员函数,但反之不能。
2.5 使用类的静态成员变量
#include <stdio.h>
class Point
{
public:
Point()
{
m_nPointCount++;
}
~Point()
{
m_nPointCount--;
}
static void output()
{
printf("%d\n", m_nPointCount);
}
private:
static int m_nPointCount;
};
void main()
{
Point pt;
pt.output();
}
编译无错误,链接错误
这是因为类的静态成员变量在使用前必须先初始化。
在main()函数前加上int Point::m_nPointCount = 0;
结论5:类的静态成员变量必须先初始化再使用。
3. 总结
结合上面的五个例子,对类的静态成员变量和成员函数作个总结:
一。静态成员函数中不能调用非静态成员。
二。非静态成员函数中可以调用静态成员。因为静态成员属于类本身,在类的对象产生之前就已经存在了,所以在非静态成员函数中是可以调用静态成员的。
三。静态成员变量使用前必须先初始化(如int MyClass::m_nNumber = 0;),否则会在linker时出错。
4. 实例:静态成员变量——链表
再给一个利用类的静态成员变量的例子以加深理解,这个例子建立一个学生类,每个学生类的对象将组成一个双向链表,用一个静态成员变量记录这个双向链表的表头,一个静态成员函数输出这个双向链表。
#include <stdio.h>
#include <string.h>
const int MAX_NAME_SIZE = 30;
class Student
{
public:
Student(char *pszName);
~Student();
public:
static void PrintfAllStudents();
private:
char m_name[MAX_NAME_SIZE];
Student *next;
Student *prev;
static Student *m_head;
};
Student::Student(char *pszName)
{
strcpy(this->m_name, pszName);
//建立双向链表,新数据从链表头部插入。
this->next = m_head;
this->prev = NULL;
if (m_head != NULL)
m_head->prev = this;
m_head = this;
}
Student::~Student ()//析构过程就是节点的脱离过程
{
if (this == m_head) //该节点就是头节点。
{
m_head = this->next;
}
else
{
this->prev->next = this->next;
this->next->prev = this->prev;
}
}
void Student::PrintfAllStudents()
{
for (Student *p = m_head; p != NULL; p = p->next)
printf("%s\n", p->m_name);
}
Student* Student::m_head = NULL;
void main()
{
Student studentA("AAA");
Student studentB("BBB");
Student studentC("CCC");
Student studentD("DDD");
Student student("MoreWindows");
Student::PrintfAllStudents();
}
程序将输出:
自己学习后,插入到链表尾来创造链表, 源码如下,删除了静态成员函数(在此例程中意义不大)
#include <iostream>
#include <string.h>
using namespace std;
///表尾插入
class Student
{
public:
Student(const char *pName);
~Student();
void PrintfAllStudents();
private:
char _name[30];
Student *next;
Student *priv;
static Student *tail;
};
Student::Student(const char *pName)
{
strcpy_s(this->_name, pName);
this->next = nullptr;
this->priv = tail;
//未更新的链表尾的上一个为当前节点
if(tail!=nullptr)
tail->next = this;
//更新下链表尾
tail = this;
}
Student::~Student() //析构过程就是节点的脱离过程
{
if(this == tail)//该节点就是尾节点
{
tail = this->priv; //上一个为尾
}
else
{
this->priv->next = this->next;
this->next->priv = this->priv;
}
}
void Student::PrintfAllStudents()
{
for(Student *p = tail; p!=nullptr; p = p->priv)
{
printf("%s\n",p->_name);
}
}
Student *Student::tail = nullptr;
int main()
{
Student studentA("AAA");
Student studentB("BBB");
Student *studentC = new Student("CCC");
Student *studentD = new Student("DDD");
Student *studentE = new Student("EEE");
studentE->PrintfAllStudents();
cout << endl;
cout << "delete studentC" <<endl;
delete studentC;
studentE->PrintfAllStudents();
cout << endl;
cout << "delete studentE" << endl;
delete studentE;
studentA.PrintfAllStudents();
return 0;
}
打印结果如下:
原文链接:C++ 类的静态成员详细讲解
本文在此基础上学习后修改。