C++类静态数据成员与类静态成员函数

类中的静态成员真是个让人爱恨交加的特性。我决定好好总结一下静态类成员的知识点,以便自己在以后面试中,在此类问题上不在被动。 
静态类成员包括静态数据成员和静态函数成员两部分。 

在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢? 

  这个问题便是本章的重点: 

  声明为static的类成员或者成员函数便能在类的范围内共同享,我们把这样的成员称做静态成员和静态成员函数。   下面我们用几个实例来说明这个问题,类的成员需要保护,通常情况下为了不违背类的封装特性,我们是把类成员设置为protected(保护状态)的,但是我们为了简化代码,使要说明的问题更为直观,更容易理解,我们在此处都设置为public。 

  以下程序我们来做一个模拟访问的例子,在程序中,每建立一个对象我们设置的类静态成员变自动加一,代码如下:

#include <iostream>  
using namespace std;  
  
class Internet  
{  
public:  
    Internet(char *name,char *address)  
    {  
        strcpy(Internet::name,name);  
        strcpy(Internet::address,address);  
        count++;  
    }  
    static void Internet::Sc()//静态成员函数  
    {  
        cout<<count<<endl;  
    }  
    Internet &Rq();  
public:  
    char name[20];  
    char address[20];  
    static int count;//这里如果写成static int count=0;就是错误的  
};  
  
Internet& Internet::Rq()//返回引用的成员函数  
{  
    return *this;  
}  
  
int Internet::count = 0;//静态成员的初始化  
void vist()  
{  
    Internet a1("中国软件开发实验室","www.cndev-lab.com");  
    Internet a2("中国软件开发实验室","www.cndev-lab.com");  
}  
void fn(Internet &s)  
{  
    cout<<s.Rq().count;  
}  
void main()  
{  
    cout<<Internet::count<<endl;//静态成员值的输出  
    vist();  
    Internet::Sc();//静态成员函数的调用  
    Internet b("中国软件开发实验室","www.cndev-lab.com");  
    Internet::Sc();  
    fn(b);  
    cin.get();  
}

  上面代码我们用了几种常用的方式建立对象,当建立新对象并调用其构造函数的时候,静态成员cout便运行加1操作,静态成员的初始化应该在主函数调用之前,并且不能在类的声明中出现,通过运行过程的观察我们发现,静态成员count的状态并不会随着一个新的对象的新建而重新定义,尽而我们了解到,所以静态成员的使用应该是类名称加域区分符加成员名称的,在上面的代码中就是Internet::count,虽然我们仍然可以使用对象名加点操作符号加成员名称的方式使用,但是不推荐的,静态态类成员的特性就是属于类而不专属于某一个对象。 


  静态成员函数的特性类似于静态成员的使用,同样与对象无关,调用方法为类名称加域区分符加成员函数名称,在上面的代码中就是Internet::Sc();,静态成员函数由于与对象无关系,所以在其中是不能对类的普通成员进行直接操作的。 

  如果上面的 static void Internet::Sc()修改成为:

 

static void Internet::Sc()//静态成员函数  
{  
    cout<<name<<endl;//错误  
    cout<<count<<endl;  
}

  静态成员函数与普通成员函数的差别就在于缺少this指针,没有这个this指针自然也就无从知道name是哪一个对象的成员了

  根据类静态成员的特性我们可以简单归纳出几点,静态成员的使用范围: 

  1.用来保存对象的个数。 

  2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态,等等。 

  3.存储链表的第一个或者最后一个成员的内存地址。 

  为了做一些必要的练习,深入的掌握静态对象的存在的意义,我们以前面的结构体的教程为基础,用类的方式描述一个线性链表,用于存储若干学生的姓名,代码如下:

#include <iostream>  
using namespace std;  
  
class Student  
{  
public:  
    Student (char *name);  
    ~Student();  
public:  
    char name[30];  
    Student *next;  
    static Student *point;  
};  
  
Student::Student (char *name)  
{  
    strcpy(Student::name,name);  
    this->next=point;  
    point=this;  
}  
  
Student::~Student ()//析构过程就是节点的脱离过程  
{  
    cout<<"析构:"<<name<<endl;  
  
    if(point==this)  
    {  
        point=this->next;  
        cin.get();  
        return;  
    }  
    for(Student *ps=point;ps;ps=ps->next)  
    {  
        if(ps->next==this)  
        {  
        cout<<ps->next<<"|"<<this->next<<endl;  
        ps->next=next;//=next也可以写成this->next;  
        cin.get();  
        return;  
        }  
    }  
    cin.get();  
}  
  
Student* Student::point=NULL;  
void main()  
{  
    Student *c = new Student("marry");  
    Student a("colin");  
    Student b("jamesji");  
    delete c;  
    Student *fp=Student::point;  
    while(fp!=NULL)  
    {  
        cout<<fp->name<<endl;  
        fp=fp->next;  
    }  
    cin.get();  
}

  从上面的代码来看,原来单纯结构化编程需要的一个链表进入全局指针在这里被类的静态成员指针所替代(类的静态成员完全可以替代全局变量),这个例子的理解重点主要是要注意观察类成员的析构顺序,通过对析构顺序的理解,使用析构函数来进行节点的脱链操作。



一 静态数据成员: 

类体中的数据成员的声明前加上static关键字,该数据成员就成为了该类的静态数据成员。和其他数据成员一样,静态数据成员也遵守public/protected/private访问规则。同时,静态数据成员还具有以下特点: 

1.静态数据成员的定义。 
静态数据成员实际上是类域中的全局变量。所以,静态数据成员的定义(初始化)不应该被放在头文件中。 
其定义方式与全局变量相同。举例如下: 

xxx.h文件 
class base{ 
private: 
static const int _i;//声明,标准c++支持有序类型在类体中初始化,但vc6不支持。 
}; 

xxx.cpp文件 
const int base::_i=10;//定义(初始化)时不受private和protected访问限制. 

注:不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif或者#pragma once也不行。 

2.静态数据成员被 类 的所有对象所共享,包括该类派生类的对象。即派生类对象与基类对象共享基类的静态数据成员。举例如下: 
class base{ 
public : 
static int _num;//声明 
}; 
int base::_num=0;//静态数据成员的真正定义 

class derived:public base{ 
}; 

main() 

base a; 
derived b; 
a._num++; 
cout<<"base class static data number _num is"<<a._num<<endl; 
b._num++; 
cout<<"derived class static data number _num is"<<b._num<<endl; 

// 结果为1,2;可见派生类与基类共用一个静态数据成员。 

3.静态数据成员可以成为成员函数的可选参数,而普通数据成员则不可以。举例如下: 
class base{ 
public : 
static int _staticVar; 
int _var; 
void foo1(int i=_staticVar);//正确,_staticVar为静态数据成员 
void foo2(int i=_var);//错误,_var为普通数据成员 
}; 

4.★静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为 所属类类型的 指针或引用。举例如下: 

class base{ 
public : 
static base _object1;//正确,静态数据成员 
base _object2;//错误 
base *pObject;//正确,指针 
base &mObject;//正确,引用 
}; 

5.★这个特性,我不知道是属于标准c++中的特性,还是vc6自己的特性。 
静态数据成员的值在const成员函数中可以被合法的改变。举例如下: 

class base{ 
public: 
base(){_i=0;_val=0;} 

mutable int _i; 
static int _staticVal; 
int _val; 
void test() const{//const 成员函数 

_i++;//正确,mutable数据成员 
_staticVal++;//正确,static数据成员 
_val++;//错误 


}; 
int base::_staticVal=0; 

二,静态成员函数 
静态成员函数没有什么太多好讲的。 

1.静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用 类成员函数指针来储存。举例如下: 
class base{ 
static int func1(); 
int func2(); 
}; 

int (*pf1)()=&base::func1;//普通的函数指针 
int (base::*pf2)()=&base::func2;//成员函数指针 


2.静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。 

3.静态成员函数不可以同时声明为 virtual、const、volatile函数。举例如下: 
class base{ 
virtual static void func1();//错误 
static void func2() const;//错误 
static void func3() volatile;//错误 
}; 

原文地址:https://www.cnblogs.com/cy568searchx/p/2955701.html