当声明一个类后,可以建立该类的多个对象,各个对象相互独立,实现了数据的封装与隐藏;但在有些情况下,类中的某一数据成员是该类所有对象共有的。此时使用静态数据成员。
C和C++允许在函数内部创建一个static对象,这个对象存储在程序的静态数据区,而不是堆栈中。 static对象只在函数第一次调用时初始化,以后它将在两次函数操作间保持它的值。

1、静态数据成员

在C++中提供了静态数据成员来解决数据为对象所共享的问题
定义格式:static 类型名 静态成员名; static int total; //类内声明
初始化:类型 类名∷静态数据成员= 初始化值; //类外初始化
——————-类内声明,类外初始化
注意:
1)静态数据成员声明时要加static说明
2)静态数据成员的初始化应在类外声明并在对象生成之前进行,默认初始化为0
3)静态数据成员在编译时创建并初始化。不能用构造函数进行初始化,不能再任何函数内分配存储空间和初始化。
4)静态数据成员属于类,不属于任何一个对象,只能再类外通过类名对它进行访问
静态数据成员的访问形式: 类名::静态数据成员;
5) 静态数据成员的主要用途时定义类的各个对象所公用的数据。
6)静态数据成员和普通数据成员一样,可以声明为public、private、protected

  1. #include <iostream>
  2. #include <iomanip>
  3. #include <string.h>
  4. using namespace std;
  5. class Student
  6. {
  7. private:
  8. char* name;
  9. int stu_no;
  10. float score;
  11. static int total; 定义静态成员
  12. public:
  13. Student(const char* na, int no, float sco); const,解决Student::Student(char [],int,float)”: 无法将参数 1 从“const char [6]”转换为“char []”
  14. void Print();
  15. };
  16. int Student::total = 0; 初始化静态成员
  17. Student::Student(const char* na, int no, float sco)
  18. {
  19. name = new char[strlen(na) + 1];
  20. strcpy(name, na);
  21. stu_no = no;
  22. score = sco;
  23. total++;
  24. }
  25. void Student::Print()
  26. {
  27. cout << "NO." << total << "student:" << name << setw(6) << stu_no << setw(6) << score << endl;
  28. cout << "Total:" << total << endl;
  29. }
  30. int main()
  31. {
  32. Student s1("张明", 1, 90);
  33. s1.Print();
  34. Student s2("王兰", 2, 95);
  35. s2.Print();
  36. Student s3("于敏", 3, 87);
  37. s3.Print();
  38. return 0;
  39. }
  40. 运行结果:
  41. NO.1student:张明 1 90
  42. Total:1
  43. NO.2student:王兰 2 95
  44. Total:2
  45. NO.3student:于敏 3 87
  46. Total:3
  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. static int a;
  7. int b;
  8. public:
  9. A(int i, int j);
  10. void show();
  11. };
  12. int A::a; 类外初始化,默认为0
  13. A::A(int i, int j)
  14. {
  15. a = i; 这里不是初始化,而是修改
  16. b = j;
  17. }
  18. void A::show()
  19. {
  20. cout << "This is static a:" << a << endl;
  21. cout << "This is non-static b:" << b << endl;
  22. }
  23. int main()
  24. {
  25. A x(1, 1);
  26. x.show();
  27. A y(2, 2);
  28. y.show();
  29. x.show();
  30. return 0;
  31. }
  32. 运行结果:
  33. This is static a:1
  34. This is non-static b:1
  35. This is static a:2
  36. This is non-static b:2
  37. This is static a:2
  38. This is non-static b:1

2、静态成员函数

  • 创建静态成员函数就是在成员函数名前用static修饰,静态成员函数时为了类的全体服务而不是为了一个类的部分对象服务,因此就不需要定义全局函数。
  • 当产生一个静态成员函数时,也就表达了一个特定类的联系。
  • 静态成员函数不能访问一般的数据成员和成员函数,它只能访问静态数据成员和其他的静态成员函数。
  • 静态成员函数是独立于类对象而存在的,因此没有this指针。
  • 未定义对象之前也可以访问静态成员函数

image.png
注意:
1)采用静态成员函数,可以在创建对象之前处理静态数据成员,这是普通成员函数不能实现的。
2)静态成员函数可以在类内定义,也可以在类外定义,在类外定义时,不要用static前缀。
3)静态成员函数在同一个类中只有一个成员函数的地址映射,节约了计算机系统的开销,提高了程序的运行效率
4)静态数据成员可以被非静态成员函数引用,也可以被静态成员函数引用。
5)但是静态成员函数不能直接访问类中的非静态成员,只能通过对象名访问(以参数对象传入)。

#include <iostream>
#include <iomanip>
#include <string.h>
using namespace std;
class Employee
{
private:
    char* name;
    int number;
    static int total;
public:
    Employee();
    static void Print();
    void Printinfo();
};

int Employee::total = 0;

Employee::Employee()
{
    name = new char[10];
    cout << "Input name and number:" << endl;
    cin >> name >> number;
    total++;
}

void Employee::Print()
{
    cout << endl << "Total:" << total << endl;
}

void Employee::Printinfo()
{
    cout << "Name:" << name << setw(7) << " " << "Number:" << number << endl;
}

int main()
{
    Employee::Print();      在未定义对象之前就可以通过类名访问静态成员函数
    int i;
    Employee s[3];
    cout << endl;
    for (i = 0; i < 3; i++)
    {
        s[i].Printinfo();
    }
    Employee::Print();
    return 0;
}


运行结果:
Total:0
Input name and number:
A
1
Input name and number:
B
2
Input name and number:
C
3

Name:A       Number:1
Name:B       Number:2
Name:C       Number:3

Total:3
void Employee::Printinfo(Employee a)
{
    cout <<endl << "Name:" << a.name << setw(7) << " " << "Number:" << a.number <<endl;
    cout << "Total:" << total <<endl;
}

int main()
{
    int i;
    Employee s;
    cout <<endl;
    Employee::Printinfo(s);
    return 0;
}

在静态成员函数Print( )中通过对象s访问了非静态成员name和number。
如果程序中没有实例化的对象,则只能通过“类名∷”访问静态成员函数;如果有实例化的对象,则既可以通过类名方式访问静态成员函数,也可以通过对象访问静态成员函数。但不建议用对象名来引用
Employee::Printinfo(s); //类名访问静态成员函数
s.PrintTotal(s); //对象访问静态成员函数
静态成员函数不含this指针。

3、静态对象

image.png
在定义对象时,可以定义类的静态对象。与静态变量一样,在定义对象时,且只是第一次时才需要执行构造函数进行初始化。静态对象的析构函数时在main结束时才自动执行的。与普通对象相同,静态对象的析构函数的执行与构造函数执行的顺序相反。

#include <iostream>
using namespace std;

class Obj
{
    private:
        char ch;
    public:
        Obj(char c);
        ~Obj();
};

void f();
void g();
Obj A('A');                //全局对象

Obj::Obj(char c):ch(c)
{
    cout << "inside construct..." << ch <<endl;
}

Obj::~Obj()
{
    cout << "inside destruct..." << ch <<endl;
}

void f()
{
    static Obj B('B');
}
void g()
{
    static Obj C('C');
}

int main()
{
    cout << "inside main()" <<endl;
    f();
    f();
    g();
    cout << "outside main()" <<endl;
    return 0;
}

运行结果:
inside construct...A
inside main()
inside construct...B
inside construct...C
outside main()
inside destruct...C
inside destruct...B
inside destruct...A

结果:
对象A是一个全局的obj类的对象,因此在main函数之前就调用A的构造函数。f()函数是定义一个静态对象B,静态对象的定义初始化只执行一次,所以第二次调用f()时就不再执行B的构造了,到g()函数,然后析构函数,与构造函数相反,先执行对象C的析构函数,然后对象B,最后全局对象A