C++ 单例模式的实现示例与相关疑问

1、实现示例

例子为c++中,有一个类CThreadManager,我想把他做成单例模式,下面给出框架示例的头文件与源文件

头文件 (CThreadManager.h):

#ifndef CTHREADMANAGER_H
#define CTHREADMANAGER_H#include <mutex>class CThreadManager {
public:// 获取单例实例static CThreadManager& getInstance();// 删除拷贝构造函数和赋值操作符,防止复制CThreadManager(const CThreadManager&) = delete;CThreadManager& operator=(const CThreadManager&) = delete;// 添加所需的成员函数void doWork(); // 示例成员函数private:// 私有构造函数和析构函数CThreadManager();~CThreadManager();// 静态成员变量static CThreadManager* instance;static std::mutex mutex_;
};#endif // CTHREADMANAGER_H

 源文件 (CThreadManager.cpp):

#include "CThreadManager.h"
#include <iostream>CThreadManager* CThreadManager::instance = nullptr;
std::mutex CThreadManager::mutex_;// 获取单例实例
CThreadManager& CThreadManager::getInstance() {if (instance == nullptr) { // 双检锁确保线程安全std::lock_guard<std::mutex> lock(mutex_);if (instance == nullptr) {instance = new CThreadManager();}}return *instance;
}// 构造函数
CThreadManager::CThreadManager() {std::cout << "CThreadManager initialized" << std::endl;
}// 析构函数
CThreadManager::~CThreadManager() {std::cout << "CThreadManager destroyed" << std::endl;delete instance; // 释放资源instance = nullptr;
}// 示例成员函数
void CThreadManager::doWork() {std::cout << "CThreadManager is working" << std::endl;
}

2、相关疑问

1)开头#ifndef CTHREADMANAGER_H #define CTHREADMANAGER_H的作用是什么

这些是头文件保护符,它们的作用是防止头文件被重复包含,从而避免编译错误。

现代 C++ 提供了 #pragma once,效果与头文件保护符相同,且更简洁。

#pragma once 是一种编译器指令,在大多数主流编译器(如 GCC(Linux系统默认编译器)、Clang、MSVC)中都能正常工作。

2)获取单例使用的是static CThreadManager& getInstance();,这与static CThreadManager* getInstance();有什么区别吗

在 C++ 中,使用 static CThreadManager& getInstance()static CThreadManager* getInstance() 的主要区别是返回值的类型——引用指针。两者在语法、内存管理和使用场景上有一些细微的差别:

1. 返回值类型是引用 (CThreadManager&)

  • 引用返回CThreadManager& getInstance() 返回一个引用,意味着你返回的是单例对象的直接引用。在调用时,不需要解引用指针,代码更加简洁。
  • 安全性:返回引用的单例通常被认为更安全,因为引用不能为 nullptr。如果你能够访问单例对象,那它一定是有效的,不需要显式地检查是否为空。避免了因访问空指针(nullptr)而引发的崩溃。
  • 语法简洁:使用引用时,访问实例的代码更直接,不需要使用箭头 -> 运算符。

示例

CThreadManager& manager = CThreadManager::getInstance();
manager.doWork();  // 直接调用成员函数

优点

  • 引用保证了你总是能访问到有效的对象。
  • 语法简洁直观。

缺点

  • 引用不能为 nullptr,这意味着如果没有正确初始化单例,代码会出错。
  • 无法显式地处理对象不存在的情况。

2. 返回值类型是指针 (CThreadManager*)

  • 指针返回CThreadManager* getInstance() 返回一个指针,意味着你获得的是单例对象的内存地址。你可以使用 nullptr 来表示单例尚未初始化的情况。
  • 灵活性:指针的返回类型在某些情况下可以提供更大的灵活性。例如,你可以返回 nullptr 来表示单例没有初始化或某些条件下的不可用状态。
  • 需要显式检查空指针:调用时必须显式检查指针是否为 nullptr,如果是空指针则不能访问对象,避免程序崩溃。

示例

CThreadManager* manager = CThreadManager::getInstance();
if (manager) {manager->doWork();  // 使用箭头操作符访问成员函数
}

优点

  • 可以明确地表示单例未初始化或不可用的状态(nullptr)。
  • 可以在函数返回前检查指针是否为 nullptr,更适合需要处理错误状态的场合。

缺点

  • 必须检查指针是否为 nullptr,增加了代码的复杂性。
  • 比起引用,指针语法稍显冗长,需要使用箭头运算符(->)来访问成员。

3. 推荐使用引用还是指针?

  • 推荐使用引用 (CThreadManager& getInstance()):在实现单例模式时,通常建议返回引用,因为引用更简洁且不需要显式地处理空值。单例模式本身通常假定对象始终存在,因此引用更符合这种假设,且访问起来更直观。

  • 使用指针 (CThreadManager* getInstance()) 适合那些需要处理单例可能尚未初始化的情况,或者当单例的存在性是可选的,返回指针可以提供更多的灵活性。

总结:

  • static CThreadManager& getInstance() 返回引用,通常更简洁、安全,适用于你假设单例对象始终有效的场景。
  • static CThreadManager* getInstance() 返回指针,适用于你可能需要返回空指针来表示单例不可用的情况,或者需要处理不同状态下的初始化。

大多数情况下,使用返回引用的方式(CThreadManager& getInstance())会更简单且符合单例模式的惯例,除非有特殊需求需要返回指针。

3)CThreadManager(const CThreadManager&) = delete; CThreadManager& operator=(const CThreadManager&) = delete;这两句的目的是什么

这两句代码的目的是防止对象被复制或赋值,确保单例模式的唯一性。

单例模式的核心要求:

  • 单例模式的核心是全局唯一性,整个程序中只允许创建一个实例。
  • 如果允许通过拷贝构造或赋值操作复制单例对象,会破坏单例模式的约束,从而导致多个实例的存在。

4)CThreadManager 的构造函数与析构函数都变为私有,目的是保证只有通过getInstance()来进行构造吗

  • 构造函数析构函数 设为私有,确保外部无法直接创建或销毁单例对象,保证了单例模式的唯一性。
  • getInstance() 是唯一的获取实例的途径,它确保了整个程序中只有一个实例,并且实例的生命周期由程序控制。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能显示屏插座:能否成为家庭用电安全的守护天使?

关键词&#xff1a;显示屏插座、LCD显示屏插座、LCD插座、智能计量插座、计量监测插座 最近&#xff0c;一则令人揪心的新闻在网络上疯传 在一个老旧小区里&#xff0c;由于电线老化和插座过载问题&#xff0c;引发了一场小型火灾。火势迅速蔓延&#xff0c;虽然幸运的是没有…

SAP_MM/CO模块-超详细的CK11N/CK40N取值逻辑梳理(十几种业务场景,1.76W字)

一、业务背景 财务月结完成后,对次月物料进行成本发布时,经常会提物料成本不准的问题,譬如说同一个物料,CK40N发布的成本与CK11N发布的成本对不上;再有就是因为物料有多个生产版本,多个采购价格,多个货源清单等主数据,导致CK11N发布成本的时候,跟用户理解的取数逻辑对…

今天你学C++了吗?——C++中的类与对象(第二集)

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

部署实战(二)--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

微信小程序+Vant-自定义选择器组件(多选

实现效果 无筛选&#xff0c;如有需要可参照单选组件中的方法.json文件配置"component": true,columns需要处理成含dictLabel和dictValue字段&#xff0c;我是这样处理的&#xff1a; let list arr.map(r > {return {...r,dictValue: r.xxxId,dictLabel: r.xxx…

.NET Core发布网站报错 HTTP Error 500.31

报错如图&#xff1a; 解决办法&#xff1a; 打开任务管理器》》服务》》找到这仨服务&#xff0c;右键启动即可&#xff0c;如果已经启动了就重启&#xff1a;

Canvas 前端艺术家

目前各种数据来看&#xff0c;前端未来在 数据可视化 和 AI 这两个领域会比较香&#xff0c;而 Canvas 是 数据可视化 在前端方面的基础技术。所以给大家唠唠Canvas这个魔幻工具。 Canvas 介绍 Canvas 中文名叫 “画布”&#xff0c;是 HTML5 新增的一个标签。Canvas 允许开发…

Leetcode142. 环形链表 II(HOT100)

链接 我的错误代码&#xff1a; class Solution { public:ListNode *detectCycle(ListNode *head) {if(!head||!head->next)return nullptr;ListNode* f head->next,*s head;while(f){f f->next,s s->next;if(!f)return nullptr;f f->next;if(fs){ListNo…

centos安装小火车

平时没事闲着 装个小火车玩-------->>>>> yum install sl.x86_64 启动命令 sl 就会出现以下场景

JavaScript的let、var、const

这张图片主要介绍了JavaScript中的三种变量声明方式&#xff1a;let、var和const。 1. let 含义&#xff1a;let是现在实际开发中常用的变量声明方式。特点&#xff1a; 块级作用域&#xff1a;let声明的变量只在其所在的块级作用域内有效。例如&#xff1a;{let x 10; } co…

替换Nacos的MySQL驱动

前言&#xff1a;替换Nacos的MySQL驱动能实现使Nacos支持MySQL8.0及以上版本的MySQL数据库 注&#xff1a;下述教程会使用命令先解压Nacos的jar包然后重新用命令把Nacos压缩成jar包&#xff0c;不然直接用压缩工具替换MySQL驱动后的Nacos是会启动不起来的&#xff08;因为没有替…

蓝桥杯每日真题 - 第21天

题目&#xff1a;(空间) 题目描述&#xff08;12届 C&C B组A题&#xff09; 解题思路&#xff1a; 转换单位&#xff1a; 内存总大小为 256MB&#xff0c;换算为字节&#xff1a; 25610241024268,435,456字节 计算每个整数占用空间&#xff1a; 每个 32 位整数占用…

AI赋能电商:构建高效、智能化的新零售生态

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;其在电商领域的应用日益广泛&#xff0c;从购物推荐到供应链管理&#xff0c;再到商品定价&#xff0c;AI正在全面改变传统电商的运营模式&#xff0c;并推动行业向智能化和精细化方向发展。本文将探讨如何利…

Python酷库之旅-第三方库Pandas(237)

目录 一、用法精讲 1116、pandas.tseries.offsets.BusinessHour.is_year_end方法 1116-1、语法 1116-2、参数 1116-3、功能 1116-4、返回值 1116-5、说明 1116-6、用法 1116-6-1、数据准备 1116-6-2、代码示例 1116-6-3、结果输出 1117、pandas.tseries.offsets.Cu…

不一样的css(三)

目录 一、前言 二、五角星 1.五角星&#xff0c;叠盖法&#xff1a; 2.五角星&#xff0c;拼凑法&#xff1a; 3.五角星&#xff0c;svg画法&#xff1a; 4.五角星&#xff0c;利用clip-path属性进行裁剪 三、结束语 一、前言 通过上两节的内容我们对css画小图标有了新…

autogen框架中使用chatglm4模型实现react

本文将介绍如何使用使用chatglm4实现react&#xff0c;利用环境变量、Tavily API和ReAct代理模式来回答用户提出的问题。 环境变量 首先&#xff0c;我们需要加载环境变量。这可以通过使用dotenv库来实现。 from dotenv import load_dotenv_ load_dotenv()注意.env文件处于…

Neural Magic 发布 LLM Compressor:提升大模型推理效率的新工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

LabVIEW实现TCP/IP通信

目录 1、TCP通信原理 2、硬件环境部署 3、云端环境部署 4、TCP通信函数 5、程序架构 6、前面板设计 7、程序框图设计 8、测试验证 本专栏以LabVIEW为开发平台&#xff0c;讲解物联网通信组网原理与开发方法&#xff0c;覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合…

Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程

目录 ⛳️推荐 前言 1. 安装docker与docker-compose 2. 启动容器运行镜像 3. 本地访问测试 4.安装内网穿透 5. 创建公网地址 6. 创建固定公网地址 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

Linux关于vim的笔记

Linux关于vim的笔记&#xff1a;(vimtutor打开vim 教程) --------------------------------------------------------------------------------------------------------------------------------- 1. 光标在屏幕文本中的移动既可以用箭头键&#xff0c;也可以使用 hjkl 字母键…