c++ ——静态成员变量和静态成员函数

前言

c++ 静态成员变量用static 关键字来声明,是整个类的数据成员,其存储不占用某个具体对象的空间,其存储在静态存储区
c++ 静态成员函数用static 关键字来声明,用于处理静态成员变量,可以用类名来调用,也可以用对象名来调用。

使用静态成员变量的目的:静态成员变量是整个类的数据成员,使用静态成员变量可以实现多个对象共享数据

测试1:

#include <iostream>
using namespace std;

class Point{
public:
    Point(int xx,int yy):x(xx),y(yy){count++;}
    Point():Point(0,0){}
    Point(const Point &p);
    ~Point(){count--;}
    void show(){cout<<x<<" "<<y<<endl;}
    static void showcount();  //用static来声明静态成员函数
private:
    int x,y;
    static int count; //用static来声明静态成员变量
};

int Point::count = 0;   //静态成员变量的定义,定义时前面不可以再有static, 且它必须要在类外面来初始化,不能在类内部初始化。静态成员变量在初始化时分配内存。

void Point::showcount() {    //静态成员函数的定义,前面不可以再有static,可以在类内部定义,也可以在类外定义。此例为在类外面进行定义。
    cout<<"count="<<count<<endl;
}

Point::Point(const Point &p){  //复制构造函数
    x = p.x;
    y = p.y;
    count++;
}

Point fun(Point m){ 
    return m;
}

int main(){
    Point::showcount(); //用     类名::函数名      来调用静态成员函数
    Point a(1,2);
    a.show();
    a.showcount();//也可用    对象名.函数名   来调用静态成员函数
    Point b(a);
    b.show();
    b.showcount();
    Point c;
    c.show();
    c.showcount();
    Point d = fun(c);
    d.show();
    d.showcount();
    return 0;
}

运行结果:
$ ./a.out
count=0
1 2
count=1
1 2
count=2
0 0
count=3
0 0
count=4

在这里插入图片描述
如图,对象a占用8个字节(成员x,y的空间,count不在里面),对象a是局部对象,存放在栈上0x7fffffffdde0的位置处。
在这里插入图片描述
count存放在静态存储区的 0x555555756134,不存储在对象中。count属于整个类,不属于某一个具体的对象。
静态成员函数也是一样,属于整个类,不属于某一个对象,所以静态成员函数可以用类名来调用,比如:Point::showcount() 。

static 成员变量必须在类声明的外部初始化,具体形式为:

 type class::name = value;

如本例:int Point::count = 0;
静态成员变量在初始化时不能再加 static,但必须要有数据类型。

static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配。没有在类外初始化的 static 成员变量不能使用,因为没分配空间。

测试2:

#include <iostream>
#include <cmath>
using namespace std;

class Point{
public:
    Point(int xx,int yy):x(xx),y(yy){count++;}
    Point():Point(0,0){}
    Point(const Point &p);
    ~Point(){count--;}
    void show(){cout<<x<<" "<<y<<endl;}
    static void showcount();
public:
    static int count;   //此例为public属性
private:
    int x,y;
};

int Point::count = 0;
 
void Point::showcount() {
    cout<<"count="<<count<<endl;
}

Point::Point(const Point &p){
    x = p.x;
    y = p.y;
    count++;
}
int main(){
    Point::showcount();
    Point a(1,2);
    cout<<a.count<<endl;  //通过对象a来访问静态成员变量。若count不是public属性,不能用对象来访问,编译时就会报错
    Point::showcount();
    Point b(3,4);
    Point::showcount();

    return 0;
}

用GDB来查看静态成员变量的存储:
在这里插入图片描述
可以看到,当b中count变为2,a中count也变为2,即a和b中的count是共享的。查看a.count和b.count的地址,可以发现是同一个地址,即是同一个存储空间。且这个存储空间是全局存储区(静态存储区),不在对象a和对象b的栈(stack)存储区。
此例中count是public,修改为protected和private,编译报错。

protected:
    static int count;

在这里插入图片描述

private:
    static int count;

在这里插入图片描述
如上图,也就是说,想通过对象来访问静态成员变量,静态成员变量只能是public属性。这一点和对象去访问普通的成员变量一样。类外面,对象只能访问public成员变量,不能访问protected和private的成员变量。

测试3:

void Point::showcount() {
    cout<<"count="<<count<<endl;
    cout<<x<<endl; //新增,尝试在静态成员函数中去访问普通的成员变量,编译报错。
}

在这里插入图片描述
也即是说,在静态成员函数中,不能去访问普通的成员变量。原因是没有this指针,不知道普通成员变量x放在哪里。
原因分析:
编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象之后通过对象来调用,因为它需要当前对象的地址。也即是有了对象,才有对象的this指针,没有对象,就无法调用普通成员函数。由于类没有this指针,所以不能用类名去调用普通成员函数,只能用实际的对象去调用普通成员函数

而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。因为静态成员函数要访问的是静态成员变量(存储在全局存储区,和this指针没毛关系)。
由于静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,所以静态成员函数不能访问普通成员变量,只能访问静态成员变量

总结:

  1. 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。注意,是共享,共享,共享,也即是只会占同一份静态存储空间
  2. static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。根本原因是静态成员变量和对象的存储空间不同,是在不同的时期去分配的
  3. 静态成员变量必须初始化,而且只能在类体外进行。例如:
    int Point::count = 0;
    初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0
    全局数据区的变量都有默认的初始值 0,而**动态数据区(堆区、栈区)**变量的默认值是不确定的,一般认为是垃圾值。
  4. 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。
    当在类外面通过对象名访问时,静态成员变量属性必须是public,对于不同的对象时,访问的是同一份内存(本质是共享)。
  5. 静态成员函数在声明时要加 static,在定义时不能加 static 。静态成员函数只能访问静态成员变量,去访问普通成员变量,编译就报错。

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/103225030