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