静态成员变量与静态成员函数

1. 基本概念

静态成员:在说明前面加了static关键字的成员。

class CRectangle
{
    private:
        int w,h;
        static int nTotalArea;        //静态成员变量,用于记录所有矩形对象的总面积
        static int nTotalNumber;      //用于记录所有矩形对象的总数目
    public:
        CRectangle(int w_,int h_);
        ~Crectangle();
        static void PrintTotal();     //静态成员函数
};
  • 区别1:普通成员变量——>每个对象有各自的一份;而静态成员变量一共就一份,为所有对象共享

注意:sizeof 运算符不会计算静态成员变量

class CMyclass{
    int n;
    static int s;
};

则sizeof(CMclass)等于4,没有计算静态成员变量s
  • 区别2:普通成员函数必须具体作用于某个对象,而静态成员函数不具体作用于某个对象。

    因此静态成员不需要通过对象就能访问。

2. 如何访问静态成员

  • 类名::成员名

    CRectangle::PrintTotal(); //访问静态成员函数

  • 对象名.成员名

    CRectangle r; r.PrintTotal();

  • 指针->成员名

    CRectangle * p =&r; p->PrintTotal();

  • 引用.成员名

    CRectangle & ref = r; int n = ref.nTotalNumber;

注意:

静态成员变量,本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。

静态成员函数,本质上是全局函数

设置静态成员这种机制的目的:将和某些类紧密相关的全局变量和全局函数写到类里面,看上去像一个整体,易于维护和理解

3. 静态成员示例

eg1:考虑一个需要随时知道矩形总数和矩形面积的图形处理程序,可以用全局变量记录总数和总面积,但是用静态成员将这两个变量封装进类中,就更容易理解和维护

class CRectangle
{
    private:
        int w,h;    //普通成员变量,每个矩形对象有各自的一份,代表各自矩形的宽和高。
        static int nTotalArea;      //两个静态成员变量,用来记录所有矩形的总面积和总数
        static int nTotalNumber; //可以用两个全局变量来代替,但是体现不出来它和CRectangle类的紧密关系,同时也容易被其他类所访问,不易维护
    public:                      //因此,把这两个变量写到CRectangle类里面,成为静态成员变量。
        CRectangle(int w_,int h_)
        ~CRectangle();
        static void PrintTotal();    //静态成员函数,只与CRectangle类有关
};

——————————

CRectangle::CRectangle(int w_,int h_)    //构造函数
{
    w=w_;
    h=h_;
    nTotalNumber ++;        //因为只有有矩形对象的生成,就一定会引发构造函数的调用,因此在构造函数里既可以增加矩形的总数和总面积。
    nTotalArea + = w*h;
}
CRectangle::~CRectangle()    //析构函数
{
    nTotalNumber --;        //由于矩形对象可能消亡,比如一个局部的矩形变量,在出了包含它的函数后就消亡了,这时矩形的总数和总面积就减少了
    nTotalArea - = w*h;   //因此需要在CRectangle类的析构函数里减少TotalNumber和TotalArea。
}
void CRectangle::PrintTotal()
{
    cout<<nTotalNumber<<","<<nTotalArea<<endl;    //打印nTotalNumber和nTotalArea
}

———————————

int CRectangle::nTotalNumber = 0;
int CRectangle::nTotalArea = 0;    //在C++中必须在定义类的文件中对静态成员变量进行一次说明或初始化,否则编译能通过但连接通不过。

int main()
{
    CRectangle r1(3,3),r2(2,2);        //定义两个矩形对象
    //cout << CRectangle::nTotalNumber;    //虽然为静态成员变量,但是main()中不能访问私有变量,因此会报错
    CRectangle::PrintTotal();    //输出2,13
    r1.PrintTotal();             //等价于上一条语句,而并非PrintTotal()作用在r1上。输出2,13
    return 0;
}
  • 在C++中,静态成员变量变必须拿到所有成员函数外面单独声明一下(类型 类名::变量名),声明的同时可以进行初始化,否则编译能通过但连接通不过。

  • 静态成员函数中,不能访问非静态成员变量,不能调用非静态成员函数,因为静态成员函数不是作用在某一个对象上的。例如接上面例子,有

void CRectangle::PrintTotal()
{
    cout<<w<<endl;    //wrong,因为PrintTotal()不是作用在某一个对象上面的
}
CRectangle::PrintTotal();    //解释不通,w到底是属于那个对象的

注意:eg1中存在严重缺陷,会导致某时刻输出值不正确,因为忽略了复制构造函数

  • 自己写了构造函数,因此编译器不会生成无参构造函数,但是编译器依然会生成复制构造函数。在使用CRectangle类时,有时会调用复制构造函数生成隐藏的CRectangle对象。比如调用一个CRectangle类对象作为参数的函数时,调用一个CRectangle类对象作为返回值的函数时。这样就会导致总数和总面积比实际情况要少(生成对象的时候没有增加总是,但消亡的时候减掉了总数)

  • 此外,临时对象在消亡的时候也会调用析构函数(例如一个函数的返回值是对象的话,则这个函数的返回值就是一个临时对象),减少nToTalNumber和nTotalArea的值,可是这些临时对象在生成时并没有增加nTotalNumber和nTotalArea的值。

  • 解决办法:为CRectangle类写一个复制构造函数,在这个复制构造函数里面,除了做复制的工作以外,还要对这个两个总数进行修改,这个时候就不会发生对象生成没有增加总数,对象消亡却减掉了总数这个怪异的现象。

    CRectangle::CRectangle(CRectangle & r)
    {
        w=r.w;h=r.h;
        nTotalNumber ++;
        nTotalArea + =w*h;
    }
    

视频地址:https://courses.edx.org/courses/course-v1:PekingX+04831750.1x+2015T1/courseware/f0484398342241b7be57dfa9f31a2e65/188ff44d5a1e4114946741803abaf969/?activate_block_id=block-v1%3APekingX%2B04831750.1x%2B2015T1%2Btype%40sequential%2Bblock%40188ff44d5a1e4114946741803abaf969

2017.06.21

results matching ""

    No results matching ""