您的位置:首页 > 娱乐 > 八卦 > 做公司官网需要哪些技术_惠州大亚湾疫情最新消息_个人网页制作完整教程_网站网上推广

做公司官网需要哪些技术_惠州大亚湾疫情最新消息_个人网页制作完整教程_网站网上推广

2024/12/31 5:53:40 来源:https://blog.csdn.net/hope_wisdom/article/details/144331953  浏览:    关键词:做公司官网需要哪些技术_惠州大亚湾疫情最新消息_个人网页制作完整教程_网站网上推广
做公司官网需要哪些技术_惠州大亚湾疫情最新消息_个人网页制作完整教程_网站网上推广

概述

        在进行大型项目的系统架构设计时,确保某些类只有一个实例,是非常重要的。比如:日志记录器、数据库连接池、配置管理器等组件,通常只需要一个实例来处理所有请求。在这种情况下,如果每次使用都创建新的对象实例,不仅会浪费系统资源,还可能导致数据不一致。为了解决这一问题,单例模式应运而生。

基本原理

        单例模式的核心在于控制类的实例化过程,确保在整个应用程序生命周期内只存在一个实例,并提供一个全局访问点。为了达到这个目的,单例模式通常包括以下几个核心特征。

        私有化构造函数:防止外部代码直接调用构造函数创建新实例。

        静态成员变量:用于存储唯一的类实例。

        公共静态方法:提供获取唯一实例的方法,通常是Singleton函数或GetInstance函数。

        懒加载机制:只有当需要时才创建实例,以提高性能。

线程安全

        需要特别注意的是,单例模式在多线程环境下可能会出现问题。具体来说,如果两个线程几乎同时调用了 Singleton函数或GetInstance函数,那么就可能出现两个线程都判断静态成员变量为NULL,并分别创建新实例的情况,从而违反了单例模式的基本要求。

        为了避免这种情况,我们可以引入线程同步机制。一种常见做法是在Singleton函数或GetInstance函数中使用互斥锁,以确保同一时间只有一个线程能够进入临界区。然而,这样做会带来额外的性能开销,尤其是在高并发场景下。因此,更推荐的做法是采用双重检查锁定技术,它可以在保持线程安全的同时尽量减少锁的竞争。具体如何使用,可以参考下面的示例代码。

#include <iostream>
#include <mutex>using namespace std;class CLogger
{
public:static CLogger *Singleton(){if (s_pSingleton == NULL){// 第一次检查lock_guard<mutex> lock(s_mutexLog);if (s_pSingleton == nullptr){// 第二次检查s_pSingleton = new CLogger();}}return s_pSingleton;}void Info(const string& strMsg){cout << "Info: " << strMsg << endl;}private:CLogger() {}static CLogger* s_pSingleton;static mutex s_mutexLog;
};CLogger* CLogger::s_pSingleton = NULL;
mutex CLogger::s_mutexLog;int main()
{CLogger::Singleton()->Info("Hello, Hope_Wisdom");return 0;
}

        在上面的示例代码中,我们实现了一个可多线程使用的日志单例类。通过两次检查s_pSingleton是否为NULL,我们确保了即使多个线程同时到达临界区,也只会有一个线程真正执行创建实例的操作。

实战解析

        假如我们有很多个类都需要实现单例功能,那么,每个类都像上面的日志类CLogger那样去实现,将是非常繁琐而冗余的。能否编写一个单例基类,其他需要实现单例功能的类,都从该基类派生呢?

        答案是肯定的。实现一个通用的单例模式基类,可以让其他类继承它以获得单例行为,这是一种优雅且可复用的设计方法。在C++中,我们可以通过模板来创建一个泛型的单例基类,这样可以确保任何派生类都能自动具备单例特性。

        除了创建实例外,单例模式还需要考虑如何正确地销毁实例,以避免内存泄漏。在C++中,由于没有垃圾回收机制,我们必须手动去管理对象的生命周期。对于单例模式,通常有以下两种方式来处理这个问题。

        1、全局静态对象:将单例实例声明为全局静态变量,这样它会在程序启动时自动初始化,并在程序结束时自动销毁。这种方式虽然简单易行,但缺乏灵活性,特别是当单例依赖其他资源或其他单例时(无法保证销毁的先后顺序)。

        2、动态创建 + 显式销毁:使用new关键字动态分配内存给单例实例,并在适当的时候通过delete来释放。

        综合以上这些考虑,我们在下面的BaseSingleton.h文件中实现了一个单例模式的基类模板。它提供了三个静态成员函数:Open函数用于动态创建单例对象,Close函数用于显式销毁单例对象,Singleton函数用于获取单例对象的指针。

        在下面的实现中,没有使用锁来保护单例对象,这是我们特意为之的。原因有二:一是可以省去锁的开销;二是创建和销毁的接口相对应,提醒用户在主程序的最开始和结束处调用Open和Close函数。

#pragma oncetemplate<typename Derived>
class CBaseSingleton
{
public:static void Open(){if (s_pSingleton == NULL){s_pSingleton = new Derived();}}static void Close(){if (s_pSingleton != NULL){delete s_pSingleton;s_pSingleton = NULL;}}static Derived *Singleton(){return s_pSingleton;}virtual ~CBaseSingleton() {}protected:CBaseSingleton() {}CBaseSingleton(const CBaseSingleton&) = delete;CBaseSingleton& operator=(const CBaseSingleton&) = delete;private:static Derived* s_pSingleton;
};template<typename Derived>
Derived* CBaseSingleton<Derived>::s_pSingleton = NULL;

        接下来,我们重新实现了CLogger类。可以看到,CLogger类单例功能的实现非常简单,从CBaseSingleton<CLogger>类派生,并声明下友元类即可。在main函数中,我们在最开始调用了Open函数,然后使用Singleton函数访问单例对象,最后在结束处调用了Close函数。

#include <iostream>
#include <string>
using namespace std;#include "BaseSingleton.h"class CLogger : public CBaseSingleton<CLogger>
{friend class CBaseSingleton<CLogger>;
public:void Info(const string& strMsg){cout << "Info: " << strMsg << endl;}private:CLogger() {}
};int main()
{CLogger::Open();CLogger::Singleton()->Info("Hello, Hope_Wisdom");CLogger::Close();return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com