当前位置: 首页 > 编程笔记 >

C++:单例模式

南门棋
2023-04-24

单例模式的定义

确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例。

方法一:

static Singleton *getInstance()
    {
        static Singleton locla_s;
        return &locla_s;
    }

该代码可能在c++11之前的版本导致多次构造函数的调用,所以只能在较新的编译器上使用。

方法二:

static Singleton *getInstance()
    {
        pthread_mutex_lock(&mutex);
        if (local_instance == nullptr)
        {
            local_instance = new Singleton();
        }
        pthread_mutex_unlock(&mutex);
        return local_instance;
    }

下面这个版本使用了mutex以及静态成员来析构单例。该方案的劣处在于锁导致速度慢,效率低。但是至少是正确的,也能在c++11之前的版本使用。

方法三:

static Singleton *getInstance()
    {
        if(local_instance == nullptr){
            pthread_mutex_lock(&mutex);
            if (local_instance == nullptr)
            {
                local_instance = new Singleton();
            }
            pthread_mutex_unlock(&mutex);
        }
        return local_instance;
    }

使用双锁检查导致未初始化的内存访问

使用如下的代码来实现已经初始化的对象的直接返回。可以使上述代码性能会大大加快。但是相同的代码在Java下面有很明显的问题,由于CPU乱序执行,可能导致访问到未经初始化的对象的引用。c++也存在相同的问题,可能导致未定义行为导致段错误

假如线程A进入锁内并分配对象的空间,但是由于指令可能乱序,实际上导致local_instance被先指向一块未被分配的内存,然后再在这块内存上进程初始化。但是在指向后,未初始化前,另一线程B可能通过getInstance获取到这个指针。

方法四

在新的标准中,atomic类实现了内存栅栏,使得多个核心访问内存时可控。这利用了c++11的内存访问顺序可控。

#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>

using namespace std;
class Singleton
{
private:
	static atomic<Singleton*> instance;

	class rememberFree {
	public:
		rememberFree() {}
		~rememberFree() {
			Singleton* local_instance = 
   instance.load(std::memory_order_relaxed);
			if (local_instance != nullptr) {
				delete local_instance;
			}
		}
	};
	static rememberFree remember;

public:
	static Singleton *getInstance()
	{
		Singleton* tmp = instance.load(std::memory_order_relaxed);
		atomic_thread_fence(memory_order_acquire);
		if (tmp == nullptr) {
			static mutex mtx;
			lock_guard<mutex> lock(mtx);
			tmp = instance.load(memory_order_relaxed);
			if (tmp == nullptr)
			{
				tmp = new Singleton();
				atomic_thread_fence(memory_order_release);
				instance.store(tmp, memory_order_relaxed);
			}
		}
		return tmp;
	}
};

atomic<Singleton*> Singleton::instance;
Singleton::rememberFree Singleton::remember;

int main()
{
	Singleton * s2 = Singleton::getInstance();
}

RAII方式同步线程锁

通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()

template <class T>
class Guard
{
public :
        Guard(const T & lock);
        virtual ~Guard();
private:
        const T & m_lock;
};
template <class T>
Guard<T>::Guard(const T & lock) :
        m_lock(lock)
{
        m_lock.acquire();
}
template <class T>
Guard<T>::~Guard()
{
    m_lock.release();
}
我们可以在应用程序中这样使用它:
 void testFunc(.....)
{
  Guard<MutexWrapper>  guard(mutex);
  ...
}
 类似资料:
  • 本文向大家介绍c# 单例模式的实现,包括了c# 单例模式的实现的使用技巧和注意事项,需要的朋友参考一下   记一下学习单例模式的笔记:   单例就是要保证该类仅有一个实例。实现完全封闭的单例(外部不能new)其实就要两点要求: 全局访问:需要一个该类型的全局静态变量,每次获取实例时都要判断它是否null,不存在new,存在通过一个方法直接返回该值获取实例来保证对象唯一; 实例化控制:new实例不能

  • 本文向大家介绍C#单例模式(Singleton Pattern)实例教程,包括了C#单例模式(Singleton Pattern)实例教程的使用技巧和注意事项,需要的朋友参考一下 本文以实例形式讲述了C#单例模式(Singleton Pattern)的实现方法,分享给大家供大家参考。具体实现方法如下: 一般来说,当从应用程序全局的角度来看,如果只允许类的一个实例产生,就可以考虑单例模式。 1.即时

  • 本文向大家介绍C#设计模式之单例模式实例讲解,包括了C#设计模式之单例模式实例讲解的使用技巧和注意事项,需要的朋友参考一下 前言 最近开始花点心思研究下设计模式,主要还是让自己写的代码可重用性高、保证代码可靠性。所谓设计模式,我找了下定义:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工

  • 本文向大家介绍C#单例模式(Singleton Pattern)详解,包括了C#单例模式(Singleton Pattern)详解的使用技巧和注意事项,需要的朋友参考一下 (新手写博客,主要是对自己学习的归纳总结。会对很多小细节详解。) 单例模式的定义: 确保一个类只有一个实例,并提供一个全局访问点。 首先实例大家应该都明白就是类生成对象的过程简单的就是String s=new String(),

  • 本文向大家介绍解析C#设计模式之单例模式,包括了解析C#设计模式之单例模式的使用技巧和注意事项,需要的朋友参考一下   单例模式(Singleton),故名思议就是说在整个应用程序中,某一对象的实例只应该存在一个。比如,一个类加载数据库中的数据到内存中以提供只读数据,这就很适合使用单例模式,因为没有必要在内存中加载多份相同的数据,另外,有些情况下不允许内存中存在多分份相同的数据,比如数据过大,内存

  • 本文向大家介绍c# 单例模式的实现方法,包括了c# 单例模式的实现方法的使用技巧和注意事项,需要的朋友参考一下 单例模式大概是所有设计模式中最简单的一种,如果在面试时被问及熟悉哪些设计模式,你可能第一个答的就是单例模式。 单例模式的实现分为两种:饿汉式和懒汉式。前者是在静态构造函数执行时就立即实例化,后者是在程序执行过程中第一次需要时再实例化。两者有各自适用的场景,实现方式也都很简单,唯一在设计时