单例模式、工厂模式 c++关键字 static

static 关键字的作用:

主要作用在于 控制变量或函数的作用域生命周期以及它们如何被不同部分的程序访问,从而帮助程序员管理内存、避免命名冲突,并实现特定的设计模式(如单例模式)。

  • 1. 静态局部变量:当在一个函数内部声明一个静态局部变量(如下面单例模式示例中的static BoxFactory boxFactory;),这个变量的生命周期将延伸到包含它的函数结束之后。这样的静态局部变量在程序运行期间只会被初始化一次,即使函数被多次调用。它保留了上一次函数调用结束时的值。

  • 2.静态全局变量:在文件作用域声明的静态变量(即在函数外部,但用static修饰),其作用范围限制在定义它的源文件内,即它是“内部链接”的。这意味着其他源文件中的代码不能直接访问这个变量,有助于减少命名冲突并封装数据。

  • 3.静态成员变量:在类声明中使用static声明的成员变量不属于类的任何特定实例,而是属于整个类。所有类实例共享同一个静态成员变量的副本。静态成员变量必须在类定义之外初始化。静态成员可以通过类名直接访问,无需实例化对象

  • 4.静态成员函数:静态成员函数也是类的一部分,但不与类的任何特定实例关联。它们不能访问非静态成员变量(因为非静态成员变量是特定于实例的),但可以访问静态成员变量和其他静态成员函数。静态成员函数同样通过类名直接调用,无需实例化对象

  • 5.静态外部变量声明:在头文件中使用extern和static声明变量,可以让多个源文件共享同一变量的声明,但每个使用该声明的源文件需要有一个独立的定义(没有static),以避免多重定义错误。这种方式主要用于跨文件共享常量或全局数据。

static 关键字示例:

1.静态局部变量

下面代码中有两个函数fun1和fun2,其中fun2中的局部变量b用static关键字修饰成静态局部变量,那么变量b不是在栈区上创建的,而是在静态区上创建的。在静态区上创建的变量,生命周期是全局的,也就是说,静态局部变量的生命周期和程序的生命周期相同
我们分别运行五次fun1()和fun2(),会发现b只会被创建一次,后面执行fun2()函数时由于存储在静态区没有被销毁,所以还是使用的已经存在的b并不会重新创建,理解这一点对单例模式中全局唯一一次类的实例化很关键

void fun1() {
	int a = 0;
	a++;
	printf(" a = %d   | ", a);
}

void fun2() {
	static int b = 0;
	b++;
	printf(" b = %d   | ", b);
}

void main()
{
	for (size_t i = 0; i < 10; i++)
	{
		if (i < 5) fun1();
		else fun2();
	}
}

在这里插入图片描述

2.静态全局变量

静态全局变量的例子就不举例了,很多理解,被static修饰的全局变量只能在本cpp文件中访问。

3.静态成员函数,静态成员变量

设计模式中的工厂模式+单例模式很好解释静态成员函数的用法:
下面的代码块有四个类:
我们可以通过工厂类可以实例化圆形 和正方形,但每次用工厂类的时候,我们不需要每次都实例化一个工厂,因此工厂类就用到了单例模式,使工厂在整个系统的生命周期唯一,避免多次创建。
在这里插入图片描述
此外阅读时注意工厂类中getInstance()使用static 修饰,

	static ShapeFactory& getInstance() //静态成员函数
	{
		static ShapeFactory instance;  //静态成员变量
		return instance;
	}
  • 确保唯一性:通过将getInstance方法声明为静态成员函数,可以不依赖于类的任何实例直接访问。这使得无论何时何地调用getInstance,都只会返回同一个ShapeFactory实例,保证了全局唯一性。
  • 全局访问:静态方法可以在不创建类实例的情况下被调用,因此可以在程序的任何地方访问工厂类的单例,便于统一管理和协调对象的创建过程。
    因此在调用时还可以写成:
int main()
{
	
	auto circle = ShapeFactory::getInstance().createShape("Circle");
	if (circle)
	{
		circle->draw();
	}

	auto square = ShapeFactory::getInstance().createShape("Circle");
	if (square)
	{
		square->draw();
	}

	return 0;
}
#include <iostream>
#include <iostream>
#include <memory>
using namespace std;


// 抽象形状类
class Shape
{
public:
	virtual ~Shape() {}
	virtual void draw() const = 0;
};

// 圆形类
class Circle : public Shape
{
public:
	void draw() const override
	{
		std::cout << "Drawing Circle" << std::endl;
	}
};

// 正方形类
class Square : public Shape
{
public:
	void draw() const override
	{
		std::cout << "Drawing Square" << std::endl;
	}
};

// 形状工厂类 - 单例模式
class ShapeFactory
{
public:
	static ShapeFactory& getInstance()
	{
		static ShapeFactory instance;
		return instance;
	}

	Shape* createShape(const std::string& shapeType)
	{
		if (shapeType == "Circle")

		{
			Circle* pCircle = new Circle();
			return pCircle;
		}
		else if (shapeType == "Square")
		{
			Square* pSquare = new Square();
			return pSquare;
		}
		else
		{
			return NULL;
		}
	}


private:
	ShapeFactory() = default;								// 私有构造函数
	ShapeFactory(const ShapeFactory&) = delete;			// 禁止拷贝构造
	ShapeFactory& operator=(const ShapeFactory&) = delete; // 禁止赋值操作
};

int main()
{
	ShapeFactory& factory = ShapeFactory::getInstance(); // 获取工厂单例
	auto circle = factory.createShape("Circle");
	if (circle)
	{
		circle->draw();
	}

	auto square = factory.createShape("Square");
	if (square)
	{
		square->draw();
	}

	return 0;
}

我们可以通过一下代码来验证工厂类是否在全局中只创建了一次

// 形状工厂类 - 单例模式
class ShapeFactory
{
public:
	static ShapeFactory& getInstance()
	{
		static ShapeFactory instance;
		return instance;
	}

	Shape* createShape(const std::string& shapeType)
	{
		if (shapeType == "Circle")

		{
			Circle* pCircle = new Circle();
			return pCircle;
		}
		else if (shapeType == "Square")
		{
			Square* pSquare = new Square();
			return pSquare;
		}
		else
		{
			return NULL;
		}
	}


private:
	ShapeFactory();// 私有构造函数
	~ShapeFactory();
	ShapeFactory(const ShapeFactory&) = delete;			// 禁止拷贝构造
	ShapeFactory& operator=(const ShapeFactory&) = delete; // 禁止赋值操作
};

ShapeFactory::ShapeFactory() {
	std::cout << "工厂类构造函数" << std::endl;
}

ShapeFactory::~ShapeFactory() {
	std::cout << "工厂析构函数" << std::endl;
}
int main()
{
	for (int i = 0; i < 5; i++) {
		ShapeFactory& factory = ShapeFactory::getInstance(); // 获取工厂单例
		std::cout << "i = " << i << std::endl;
	}
	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/707608.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

工具推荐-文件捆绑工具

前提 在之前有突发奇想过&#xff0c;有没有那种我发给别人一个pdf文件&#xff0c;别人点击后看到的是pdf文件的内容&#xff0c;我这边也看到了上线的提示。于是就去研究pdf能加入哪些特殊的功能。看了一段时间后发现pdf的一些不一样的功能 像是打开pdf后弹出一个框 或者是…

什么是端口转发?路由器如何正确的设置端口转发和范围转发?(外网访问必备设置)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 端口转发 📒🚀 端口转发的应用场景💡 路由器如何设置端口转发(示例)💡 端口范围转发(示例)🎯 范围转发的应用场景🛠️ 设置范围转发📝 范围转发实操示例🎈 注意事项 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 …

wmv转换mp4怎么操作?3个格式转换方法分享

wmv转换mp4怎么操作&#xff1f;将WMV转换为MP4格式&#xff0c;可以方便我们在多种设备和平台上流畅播放视频。MP4格式具有广泛的兼容性和优化过的编码&#xff0c;使其在各种媒体播放器、智能手机、平板电脑以及电视上都能得到良好的支持。此外&#xff0c;MP4格式的视频文件…

手机直播不用麦克风可以吗?一文看懂无线麦克风哪个好

市面上对于无线麦克风的需求有增无减&#xff0c;原因是直播、短视频行业火爆&#xff0c;许多人都开始加入这一行业&#xff0c;不过对于麦克风这类产品的疑问也越来越多。例如&#xff1a;无线麦克风怎么选&#xff1f;实不实用&#xff1f;手机直播不用麦克风可以吗&#xf…

安卓启动流程

还是以高通为例子。这次整理并不是很完善&#xff0c;下来会参考一些文档再整理。。。 高通平台启动过程_高通平台启动流程-CSDN博客 https://www.cnblogs.com/schips/p/how_qualcomm_soc_boot.html 1. 初始启动阶段&#xff08;Boot ROM&#xff09; 处理器复位&#xff1a;…

OpenCV图像变换

一 图像的缩放 resize(src,dst,dsize,fx,fy,interpolation) fx&#xff1a;x轴的缩放因子 fy&#xff1a;y轴的缩放因子 interpolation 插值算法 INTER_NEAREST,临近插值&#xff0c;速度快&#xff0c;效果差 INTER_LINEAR,双线性插值&#xff0c;原图中的4个点 INTER_CUBIC…

【原创】springboot+mysql社区住户综合管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Java接口实现与类继承

学习初期发现接口实现与类继承很像&#xff0c;随着学习深入发现它们之间的联系与区别&#xff0c;整理如下&#xff1a; 经实验发现&#xff0c;实现接口的类中含有接口中的所有属性和方法&#xff0c;继承父类的子类中也含有父类中所有的属性和方法&#xff0c;可以说接口实…

【SpringBoot + Vue 尚庭公寓实战】公寓杂费接口实现(八)

【SpringBoot Vue 尚庭公寓实战】公寓杂费接口实现&#xff08;八&#xff09; 文章目录 【SpringBoot Vue 尚庭公寓实战】公寓杂费接口实现&#xff08;八&#xff09;1、公寓杂费业务介绍2、公寓杂费逻辑模型介绍3、接口实现3.1、保存或更新杂费值3.2、保存或更新杂费名称3…

Python学习之旅:你的大学计算机专业宝藏路线图

在信息时代的浪潮中&#xff0c;Python以其强大的功能和极简的语法成为了无数程序员心中的白月光。作为大学计算机专业的学生&#xff0c;掌握Python不仅能够为未来的职业生涯铺路&#xff0c;更能让您在学术研究和实际应用中如鱼得水。今天&#xff0c;我将与大家分享一套实用…

南京观海微电子-----PCB设计怎样降低EMI

开关模式电源是AC-DC或DC-DC电源的通用术语&#xff0c;这些电源使用具有快速开关动作的电路进行电压转换/转换(降压或升压)。随着每天开发出更多的设备(潜在的EMI受害者)&#xff0c;克服EMI成为工程师面临的主要挑战&#xff0c;并且实现电磁兼容性(EMC)与使设备正常运行同等…

使用高级SQL向量查询增强您的 RAG 应用程序

通过使用 MyScale 和 LangChain 创建 AI 助手来克服 RAG 的限制&#xff0c;以提高数据检索过程的准确性和效率。 让我们讨论一下如何使用 [MyScale]创建 AI 助手来克服这些 RAG 限制&#xff0c;从而提高数据检索过程的准确性和效率。我们将抓取 Hacker News 的最新故事&#…

鸿蒙轻内核A核源码分析系列七 进程管理 (2)

本文先熟悉下进程管理的文件kernel\base\core\los_process.c中的内部接口&#xff0c;读读代码&#xff0c;做些记录。 1、LiteOS-A内核进程全局变量 ⑴是进程池&#xff0c;存放各个进程控制块LosProcessCB的信息。⑵处开始的g_freeProcess是空闲进程链表&#xff0c;挂载各…

DevExpress WPF中文教程:Grid - 如何完成列和编辑器配置(设计时)?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

【GreenHills】如何使用GHS对于不同的文件进行文档内容对比

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 利用GHS对于不同的文件进行对比。 2、 问题场景 在项目开发过程中&#xff0c;会对于工程进行版本管理&#xff0c;对于没有项目管理工具的客户&#xff0c;想要对于当前版本的源文件和上一版或其他版本的源文件进行…

RT-DETR那么火,如何使用它、如何改进它?热滚滚的使用攻略出炉了

前言 RT-DETR是百度出品的新一代检测器&#xff0c;属于DETR系列&#xff0c;即基于Transformer的目标检测框架。官方代码是基于Paddle平台的&#xff0c;当然也有Pytorch版本的&#xff0c;对于习惯使用Pytorch平台和YOLO风格的研究人员而言&#xff0c;调试和改进RT-DETR是比…

【SQL边干边学系列】08高级问题-4

文章目录 前言回顾高级问题48.客户分组49.客户分组-修复null50.使用百分比的客户分组51.灵活的客户分组 答案48.客户分组49.客户分组-修复null50.使用百分比的客户分组51.灵活的客户分组 未完待续 前言 该系列教程&#xff0c;将会从实际问题出发&#xff0c;边干边学&#xff…

c++异常处理-漏洞利用

文章目录 参考异常处理顺序eh_frame段的作用编译过程中的.eh_frame生成运行时异常处理 堆栈展开简单劫持rbpret chop1. 异常发生时的堆栈保存2. 查找.eh_frame信息3. 解析FDE并恢复CFA4. 恢复寄存器5. 堆栈展开6. 转向异常处理逻辑 先忙awd了以后补 参考 https://xz.aliyun.co…

LeetCode刷题之HOT100之二叉树的遍历

2024/6/14 这几天总是下雨&#xff0c;天气预报上面显示这个月都要持续下雨&#xff0c;下雨天了怎么办&#xff1f;我好想你&#xff0c;不敢打给你&#xff0c;我找不到原因。说着说着唱起来了哈哈&#xff01;Anyway&#xff0c;昨天晚上打开了《涅朵奇卡一个女人的一生》&a…

Vue3:解决在main.ts 中调用自定义的js文件会报错的问题

案例&#xff1a;Vue3 &#xff0c;使用的是main.ts &#xff0c;在main.ts 中调用自定义的ruoComment.js文件会报错&#xff0c; 页面报错&#xff1a; main.ts文件引用报错&#xff1a; 解决报错&#xff1a;找到tsconfig.json文件 加上如下代码&#xff1a;即可解决问题 &q…