如何实现单例模式?

server/2024/12/26 2:25:32/

概念

单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要控制资源访问的场景,如数据库连接、日志记录或者配置管理等。

方式

懒汉式单例

懒汉式单例是在第一次被请求时创建实例。为了确保线程安全,通常使用 std::mutex 进行保护。

#include <iostream>  
#include <mutex>  class Singleton {  
public:  // 禁止拷贝构造和赋值  Singleton(const Singleton&) = delete;  Singleton& operator=(const Singleton&) = delete;  static Singleton& getInstance() {  static Singleton instance; // 此时实例在第一次调用时创建  return instance;  }  void someMethod() {  std::cout << "Doing something in Singleton." << std::endl;  }  private:  Singleton() {  std::cout << "Singleton Constructor" << std::endl; // 建构函数  }  ~Singleton() {  std::cout << "Singleton Destructor" << std::endl; // 析构函数  }  
};  int main() {  Singleton::getInstance().someMethod(); // 访问单例  return 0;  
}

代码解析

  • 私有构造函数:确保外部不能直接创建实例。
  • 禁止拷贝:复制构造函数和赋值运算符被删除,以防止复制。
  • 静态局部变量:在静态方法 getInstance 中声明一个静态局部变量,这个变量 在第一次使用时初始化,后续调用将返回相同的实例。
  • 线程安全:C++11 及以上版本中,局部静态变量在多线程中是安全的,不需要额外的锁。

饿汉式单例

饿汉式单例在程序启动时就创建实例,不论是否被使用。这样可以避免多线程中的同步问题,但在资源使用上可能会浪费。

#include <iostream>  class Singleton {  
public:  // 禁止拷贝构造和赋值  Singleton(const Singleton&) = delete;  Singleton& operator=(const Singleton&) = delete;  static Singleton& getInstance() {  return instance; // 直接返回实例  }  void someMethod() {  std::cout << "Doing something in Singleton." << std::endl;  }  private:  Singleton() {  std::cout << "Singleton Constructor" << std::endl; // 建构函数  }  ~Singleton() {  std::cout << "Singleton Destructor" << std::endl; // 析构函数  }  static Singleton instance; // 静态实例  
};  // 定义静态成员  
Singleton Singleton::instance;  int main() {  Singleton::getInstance().someMethod(); // 访问单例  return 0;  
}

代码解析

  • 静态实例:在类的内部声明一个静态成员 instance,在类外定义该成员。
  • 构造函数:与懒汉式相同,构造函数和析构函数是私有的,以防止外部实例化。

线程安全的懒汉式单例(使用锁)

这种方法在创建单例实例时使用互斥锁来实现控制,确保多个线程不同时创建。

#include <iostream>  
#include <mutex>  class Singleton {  
public:  static Singleton* getInstance() {  std::lock_guard<std::mutex> lock(mu); // 加锁  if (!instance)  instance = new Singleton(); // 延迟加载  return instance;  }  void someMethod() {  std::cout << "Doing something in Singleton." << std::endl;  }  private:  Singleton() = default; // 私有构造函数  ~Singleton() = default; // 私有析构函数  Singleton(const Singleton&) = delete; // 禁止拷贝构造  Singleton& operator=(const Singleton&) = delete; // 禁止赋值  static Singleton* instance; // 静态实例指针  static std::mutex mu; // 互斥量  
};  // 定义静态成员  
Singleton* Singleton::instance = nullptr;  
std::mutex Singleton::mu;  int main() {  Singleton::getInstance()->someMethod(); // 访问单例  return 0;  
}

代码解析

  • 互斥量:使用 std::mutex 控制访问,以实现线程安全。每当访问 getInstance 时,lock_guard 自动加锁。
  • 动态分配:实例在首次访问时通过 new 创建,依赖用户在适当的位置调用 delete 销毁对象。

采用 std::call_once

C++11 中引入的 std::call_once 可以确保线程安全的单例实现。

#include <iostream>  
#include <mutex>  class Singleton {  
public:  static Singleton& getInstance() {  std::call_once(initInstanceFlag, &Singleton::initSingleton);  return *instance;  }  void someMethod() {  std::cout << "Doing something in Singleton." << std::endl;  }  private:  Singleton() {  std::cout << "Singleton Constructor" << std::endl;  }  ~Singleton() {  std::cout << "Singleton Destructor" << std::endl;  }  static void initSingleton() {  instance = new Singleton();  }  static Singleton* instance;  static std::once_flag initInstanceFlag; // 初始化标志  
};  // 静态成员初始化  
Singleton* Singleton::instance = nullptr;  
std::once_flag Singleton::initInstanceFlag;  int main() {  Singleton::getInstance().someMethod();  return 0;  
}

代码解析

  • std::once_flag:用于保证指定的函数只被调用一次。
  • std::call_once:确保在多线程环境下只会初始化一次实例。

总结

单例模式可以通过多种方式实现,包括懒汉式、饿汉式、线程安全的懒汉式、使用 std::call_once 等。选择合适的实现方式主要取决于项目的需求、线程安全需求以及如何管理生命周期等因素。对于大多数情况,使用 C++11 提供的懒汉式结合 std::call_once 是最推荐的做法,因为它既保证了线程安全,又简洁易懂。


http://www.ppmy.cn/server/153201.html

相关文章

产品更新 | 一网联千策:华望M-Cowork平台上的SysML模型协同管理

华望产品更新速递 功能介绍 | 协同平台M-Cowork的强大功能 ◆在线SysML建模与预览 ◆版本控制和基线管理 ◆可追溯的审签流程 ◆全面的系统管理 产品亮点 | 进一步了解协同平台M-Cowork ◆M-Cowork的管理功能 ◆M-Cowork的预览功能 ◆M-Cowork的审签流程 前言 在系统工…

《XML》教案 第2章 使第4章 呈现XML文档

《XML》教案 第2章 使第4章 呈现XML文档 主讲人&#xff1a; 回顾上一章: [10分钟] 2 课程知识点讲解&#xff1a; 2 通过级联样式表转换XML文档&#xff1a;[15分钟] 3 通过可扩展样式表语言转换XML文档 &#xff1a;[5分钟] 4 嵌套 for 循环 &#xff1a;[20分钟] 5 本章总结…

数据结构漫游记:静态链表的实现(CPP)

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…

postgreSql对分钟级的降雨数据进行插值为整小时

postgreSql对分钟级的降雨数据进行插值为整小时 SQL语句实现 SQL语句实现 --核查某个小流域的降雨量小时插值是否正确SELECT tm, sum(drp) as sum, round(sum(drp), 2) as drp2 from(SELECT a.stcd, (TO_TIMESTAMP(time_period, YYYY-MM-DD HH24:MI:SS) INTERVAL 1 HOUR) as t…

解决“SVN无法上传或下载*.so、*.a等二进制文件“问题

今天&#xff0c;在使用Subversion提交代码到服务器时&#xff0c;发现无法提交*.a、*.so等二进制文件&#xff0c;右击这些文件&#xff0c;发现其属性为ignores。     问题原因&#xff1a;SVN的配置文件里&#xff0c;屏蔽了*.a、*.so文件的上传与下载&#xff0c;并把这些…

04、Vue与Ajax

4.1 发送AJAX异步请求的方式 发送AJAX异步请求的常见方式包括&#xff1a; 4.1.1. 原生方式 使用浏览器内置的JS对象XMLHttpRequest const xhr new XMLHttpRequest() xhr.open() xhr.send() xhr.onreadystatechange function(){} 4.1.2. 原生方式 使用浏览器内置的JS函…

mac 上安装Selenium + 谷歌浏览器驱动 116.0.5845.x

1、本地安装Selenium pip install selenium pip show selenium 2、安装谷歌驱动 &#xff08;1&#xff09;驱动地址 https://chromedriver.storage.googleapis.com/index.html &#xff08;2&#xff09;查看谷歌版本 &#xff08;3&#xff09;选择驱动并下载 上述没有我…

ISO17025最新认证消息

ISO17025认证是国际上广泛认可的实验室管理标准&#xff0c;全称为《检测和校准实验室能力的通用要求》&#xff0c;由国际标准化组织&#xff08;ISO&#xff09;和国际电工委员会&#xff08;IEC&#xff09;联合发布。以下是对ISO17025最新认证消息及相关内容的归纳&#xf…