8.静态成员

8.面向对象-封装-静态成员


8.1 知识点

知识回顾 static变量

  • 静态变量

  • 用法:

    • static 变量类型 变量名;
  • 作用:

    • static 修饰的变量会在程序的整个生命周期内存储
    • 就算在函数内部声明,也遵循这一规则
    • 它可以用来声明静态局部变量、静态全局变量或静态类成员等
    • 声明一次,永久使用
  • 生命周期:

    • 静态局部变量的生命周期是整个程序运行期,即使函数返回,它也不会被销毁
    • 静态全局变量的生命周期也是整个程序运行期
  • 优点:

    • 静态变量只初始化一次,且一直保留值,适合全局共享信息或缓存的场景
  • 缺点:

    • 静态变量会一直占用内存空间,直到程序结束,因此会增加内存消耗
  • 注意:

    • 除非有需求,否则不要过多地使用静态变量
  • 静态全局变量和全局变量的区别:

    • 主要区别是作用域不同
    • 全局变量:可以在多个文件中访问,适合需要共享数据的情况
    • 静态全局变量:只能在定义它的文件中访问,适合只在单个源文件内部使用的数据

静态成员基本概念

  • 类成员也可以用 static 关键字修饰
  • static 修饰的成员变量、方法,称为静态成员

静态成员的特点

  • 共同特点:

    • 在声明成员(变量和函数)时,前面加上 static 关键字
    • 直接用 类名:: 来使用,也可以通过类对象访问(不推荐)
    • 生命周期贯穿程序始终(类似全局变量)
    • 属于整个类,而不是某个对象,所有对象共享同一个静态成员
  • 静态成员变量特点:

    • .h 中声明后,需要在 .cpp 中进行定义
  • 静态成员函数特点:

    • 静态成员函数中不能使用非静态成员
    • 非静态成员函数可以使用静态成员
    • 没有 this 指针
#pragma once
class Lesson08
{
public:
    int i = 10;
    // 对于静态成员变量 不能在声明时定义 需要在 cpp 中进行定义
    static float PI; // = 3.1415926f;//会报错

    // 计算圆的面积 可以声明时同时定义 也可以在 cpp 中进行定义
    static float CalcCircle(float r);
    /*{
        * 在这里是不能使用非静态成员 i 的 会报错
        return PI * r * r;
    }*/
};
#include "Lesson08.h"

// 对于静态成员的定义 不需要再在前面写 static 了

// 静态成员变量 需要在 .cpp 中进行初始化定义
float Lesson08::PI = 3.1415926f;

// 静态成员函数 可以在 .h 中定义 也可以在 .cpp 中定义 建议大家还是在 .cpp 中定义即可
float Lesson08::CalcCircle(float r)
{
    // 在静态成员方法中无法使用非静态成员变量或者方法
    // 因为非静态成员变量和方法 是基于对象的(对象实例化才会有的内容)
    // 而静态成员是基于类的 (是程序一开始就会分配内存的)
    return PI * r * r;
}
float m = Lesson08::CalcCircle(10);
cout << "半径为10的面积为:" << m << endl; // 半径为10的面积为:314.159

cout << "PI是:" << Lesson08::PI << endl; // PI是:3.14159

// 我们同样可以通过实例化的对象去使用这些成员 
// 但是不建议 因为不太好通过观察区分是否是静态
Lesson08 l;
l.CalcCircle(10);
l.PI;

Lesson08 l2;
l2.CalcCircle(10);
l2.PI = 4; // 改变PI的值

// 修改后 PI 的值打印出来都是修改后的
cout << Lesson08::PI << endl; // 4
cout << l.PI << endl;         // 4
cout << l2.PI << endl;        // 4

为什么可以不依赖对象使用静态成员(变量)

  • 记住!程序中是不能无中生有的

  • 我们要使用的对象、变量、函数,都是要在内存中分配内存空间的

  • 实例化对象的目的就是为了分配内存空间,在程序中产生一个抽象的对象

  • 静态成员的特点:

    • 程序开始运行时就会分配内存空间,存储在数据段(数据存储区),所以我们就能直接使用
    • 静态成员和程序同生共死
    • 只要使用了它,直到程序结束时内存空间才会被释放
    • 所以一个静态成员就会有自己唯一的一个“内存小房间”
    • 在任何地方使用都是用的小房间里的内容,改变了它也是改变小房间里的内容
  • 补充:

    • 以上主要是针对静态成员变量和普通成员变量区别的讲解
    • 对于成员函数和静态成员函数来说,它们都是存储在代码段的
    • 只是通过 static 来判断哪些成员函数是通过对象点出来用,哪些静态成员函数是通过类名 :: 来使用
    • 函数内部是否可以使用 this 或者成员变量也是通过 static 来判断的

静态成员对于我们的作用

  • 静态变量:

    • 常用唯一变量的声明
    • 方便别人获取的对象声明
    • 节约一定的内存空间
  • 静态方法:

    • 常用的唯一的方法声明,比如相同规则的数学计算相关函数

总结

  • 静态变量:

    • 常用唯一变量的声明
    • 方便别人获取的对象声明
    • 节约一定的内存空间
  • 静态方法:

    • 常用的唯一的方法声明,比如相同规则的数学计算相关函数

8.2 知识点代码

Lesson08.h

#pragma once
class Lesson08
{
public:
    int i = 10;
    //对于静态成员变量 不能在声明时定义 需要在cpp中进行定义
    static float PI;// = 3.1415926f;//会报错

    //计算圆的面积 可以声明时同时定义 也可以在cpp中进行定义
    static float CalcCircle(float r);
    /*{
    *   在这里是不能使用非静态成员i的 会报错
        return PI * r * r;
    }*/
};

Lesson08.cpp

#include "Lesson08.h"

//对于静态成员的定义 不需要再在前面写static了

//静态成员变量 需要在.cpp中进行初始化定义
float Lesson08::PI = 3.1415926f;

//静态成员函数 可以在.h中定义 也可以在.cpp中定义 建议大家还是在.cpp中定义即可
float Lesson08::CalcCircle(float r)
{
    //在静态成员方法中无法使用非静态成员变量或者方法
    //因为非静态成员变量和方法 是基于对象的(对象实例化才会有的内容)
    //而静态成员是基于类的 (是程序一开始就会分配内存的)
    return PI * r * r;
}

Lesson08_面向对象_封装_静态成员.cpp

#include <iostream>
#include "Lesson08.h"
using namespace std;
int main()
{
    #pragma region 知识回顾 static变量
    //静态变量
    //用法:
    // static 变量类型 变量名;
    //作用:
    // static修饰的变量会在程序的整个生命周期内存储
    // 就算在函数内部声明,也遵循这一规则
    // 它可以用来声明静态 局部变量、静态全局变量 或 静态类成员 等
    // 声明一次,永久使用
    //生命周期:
    // 静态局部变量的生命周期是整个程序运行期,即使函数返回,它也不会被销毁。
    // 静态全局变量的生命周期也是整个程序运行期

    //优点:
    // 静态变量只初始化一次,且一直保留值,适合全局共享信息或缓存的场景
    //缺点:
    // 静态变量会一直占用内存空间,直到程序结束,因此会增加内存消耗
    //注意:
    // 除非有需求,否则不要过多的使用静态变量

    //静态全局变量和全局变量的区别
    //他们的主要区别就是作用域不同

    //全局变量:
    //可以在多个文件中访问,适合需要共享数据的情况
    //静态全局变量:
    //只能在定义它的文件中访问,适合只在单个源文件内部使用的数据
    #pragma endregion

    #pragma region 知识点一 静态成员基本概念
    //类成员也可以用静态关键词static进行修饰
    //用static修饰的 成员变量、方法
    //称为静态成员
    #pragma endregion

    #pragma region 知识点二 静态成员的特点
    //共同特点:
    // 1.在声明成员(变量和函数)时,前面加上static关键字
    // 2.直接用类名::出使用,也可以通过类对象访问(不推荐)
    // 3.生命周期贯穿程序始终(类似全局变量)
    // 4.属于整个类,而不是某个对象,所有对象共享同一个静态成员

    //静态成员变量特点:
    //在.h中声明后,需要在.cpp中进行定义

    //静态成员函数特点:
    //1.静态成员函数中不能使用非静态成员
    //2.非静态成员函数可以使用静态成员
    //3.没有this指针

    float m = Lesson08::CalcCircle(10);
    cout << "半径为10的面积为:" << m << endl;//半径为10的面积为:314.159

    cout << "PI是:" << Lesson08::PI << endl;//PI是:3.14159

    //我们同样可以通过实例化的对象去使用这些成员 
    //但是不建议 因为不太好通过观察区分是否是静态
    Lesson08 l;
    l.CalcCircle(10);
    l.PI;

    Lesson08 l2;
    l2.CalcCircle(10);
    l2.PI = 4;//改变PI的值

    //修改后PI的值打印出来都是修改后的
    cout << Lesson08::PI << endl;//4
    cout << l.PI << endl;//4
    cout << l2.PI << endl;//4

    #pragma endregion

    #pragma region 知识点三 为什么可以不依赖对象使用静态成员(变量)
    //记住!
    //程序中是不能无中生有的
    //我们要使用的对象,变量,函数都是要在内存中分配内存空间的
    //之所以要实例化对象,目的就是分配内存空间,在程序中产生一个抽象的对象

    //静态成员的特点
    //程序开始运行时 就会分配内存空间,存储在数据段(数据存储区),所以我们就能直接使用。
    //静态成员和程序同生共死
    //只要使用了它,直到程序结束时内存空间才会被释放
    //所以一个静态成员就会有自己唯一的一个“内存小房间”
    //这让静态成员就有了唯一性
    //在任何地方使用都是用的小房间里的内容,改变了它也是改变小房间里的内容。

    //补充:
    //以上主要是针对静态成员变量和普通成员变量区别的讲解

    //对于成员函数和静态成员函数来说 他们都是存储在代码段的
    //只是通过static来判断 哪些成员函数是通过对象点出来用 哪些静态成员函数是通过类名::来使用
    //函数内部是否可以使用this或者成员变量也是通过static来判断的
    #pragma endregion

    #pragma region 知识点四 静态成员对于我们的作用
    //静态变量:
    //1.常用唯一变量的声明 
    //2.方便别人获取的对象声明
    //3.节约一定的内存空间
    //静态方法:
    //常用的唯一的方法声明 比如 相同规则的数学计算相关函数
    #pragma endregion

    //总结
    //             普通成员变量                           静态成员变量
    //存储区别    堆、栈,和对象同生共死             数据段、和程序同生共死
    //使用区别    需要通过对象点出                  通过类名::出(也可以通过对象点出,但不建议)
    //唯一性区别   每个对象都是独有的特征             共用的唯一的特征或行为的体现(一般用于存储数学或物理公式中不变的数值)

    //                      普通成员方法                           静态成员方法
    //存储区别                             都在代码段
    //函数内部逻辑区别    是可以使用普通或静态成员的                只能使用静态成员
    //函数使用区别        需要通过对象点出                 通过类名::出(也可以通过对象点出,但不建议)
    //多个对象中的节约性  如果使用静态成员方法 可以把一些共用的逻辑提取处理 作为唯一的一个函数
    //                   这样就不需要再多个类中重复书写相同代码了 想要跨类使用 直接 通过类名::来使用即可
}

8.3 练习题


静态成员和普通成员的区别

成员变量对比

特性 普通成员变量 静态成员变量
存储位置 堆/栈,与对象同生共死 数据段,与程序同生共死
使用方式 通过对象点出(对象.变量 通过类名作用域(类名::变量),也可通过对象点出(不建议)
唯一性 每个对象独立拥有 所有对象共用唯一实例,常用于存储不变的数值(如数学/物理公式)

成员方法对比

特性 普通成员方法 静态成员方法
存储位置 共用代码段(所有成员方法均存储于此)
函数内部逻辑 可访问普通成员和静态成员 仅能访问静态成员
调用方式 通过对象点出(对象.方法() 通过类名作用域(类名::方法()),也可通过对象点出(不建议)
设计优势 —— 提取共用逻辑,跨类调用时直接通过类名::访问,提升代码复用性

使用静态成员实现单例模式

#pragma once
#include <iostream>
using namespace std;

class Test
{
private:
    // 私有化构造函数,不能在外部实例化
    Test();
    static Test* instance;

public:
    int i = 10;
    string name = "韬老狮";

    // 直接通过该类类名就能够得到唯一的对象
    static Test* GetInstance();
};
#include "Test.h"

// 饿汉模式:只能在内部调用私有构造去声明一个对象
Test* Test::instance = new Test();

Test::Test()
{
}

Test* Test::GetInstance()
{
    // 静态局部变量只会在第一次调用时初始化,之后都不会重新初始化,
    // 并且会伴随整个程序的生命周期
    // 在整个应用程序的生命周期中,有且仅会有一个该对象的存在

    // 第一种方式:懒汉模式
    // static Test instance;
    // return &instance;

    // 第二种方式:饿汉模式
    return instance;
}
// 以下操作演示使用:
cout << Test::GetInstance()->i << endl;       // 10
cout << Test::GetInstance()->name << endl;    // 韬老狮

Test* ptr = Test::GetInstance();  // 得到指向 instance 的地址
ptr = nullptr;                    // 这样置空只是置空指针,断开指向 instance 的地址,instance 对象仍然存在


8.4 练习题代码

Test.h

#pragma once
#include <iostream>
using namespace std;
class Test
{
private:
    //私有化构造函数 不能让在外部实例化
    Test();
    static Test* instance;

public:
    int i = 10;
    string name = "韬老狮";

    //2.直接通过该类类名就能够得到唯一的对象
    static Test* GetInstance();
    
};

Test.cpp

#include "Test.h"

//只能在内部调用私有构造去声明一个对象 饿汉模式
Test* Test::instance = new Test();

Test::Test()
{

}

Test* Test::GetInstance()
{
    //静态局部变量 会只有第一次才会初始化 之后都不会初始化了
    //并且会伴随整个程序的生命周期
    //在整个应用程序的生命周期中,有且仅会有一个该对象的存在

    // 第一种方式 懒汉模式
    //static Test instance;
    //return &instance;

    //第二种方式
    return instance;
}

Lesson08_练习题.cpp

#include <iostream>
#include "Test.h"
using namespace std;
int main()
{
    #pragma region 练习题一
    /*请简单描述
    静态成员和普通成员的区别*/

    //             普通成员变量                           静态成员变量
    //存储区别    堆、栈,和对象同生共死             数据段、和程序同生共死
    //使用区别    需要通过对象点出                  通过类名::出(也可以通过对象点出,但不建议)
    //唯一性区别   每个对象都是独有的特征             共用的唯一的特征或行为的体现(一般用于存储数学或物理公式中不变的数值)

    //                      普通成员方法                           静态成员方法
    //存储区别                             都在代码段
    //函数内部逻辑区别    是可以使用普通或静态成员的                只能使用静态成员
    //函数使用区别        需要通过对象点出                 通过类名::出(也可以通过对象点出,但不建议)
    //多个对象中的节约性  如果使用静态成员方法 可以把一些共用的逻辑提取处理 作为唯一的一个函数
    //                   这样就不需要再多个类中重复书写相同代码了 想要跨类使用 直接 通过类名::来使用即可
    #pragma endregion

    #pragma region 练习题二
    /*请用静态成员相关知识实现
    一个类对象,在整个应用程序的生命周期中,
    有且仅会有一个该对象的存在,不能在外部实例化,直接通过该类类名就能够得到唯一的对象*/

    //1.不能在外部实例化
    /*Test t;
    Test t2 = new Test();*/

    cout << Test::GetInstance()->i << endl;//10
    cout << Test::GetInstance()->name << endl;//韬老狮

    Test* ptr = Test::GetInstance();//得到指向instance的地址
    ptr = nullptr;//这样置空只是置空指针 断开指向instance的地址 instance对象仍然存在
    #pragma endregion

}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏