单例模式的饿汉和懒汉写法(基于C++)


单例模式

单例模式确保一个类只有一个实例,并提供全局访问点。这样可以避免在系统中出现多个相同的对象,从而提高系统的性能和可维护性。
单例模式的实现包括饿汉和懒汉,下面介绍C++中这两种方式的写法。

例程

饿汉

饿汉模式是在程序启动前创建实例。
下面是一个全局计数器的示例。
counter.h

class Counter
{
    
    
public:
    static void plus(int number);
    static void print();

private:    //method
    Counter();

private:    //member
    static Counter counter;
    int value {
    
     0 };
};

counter.cpp

#include "counter.h"
#include <iostream>

using namespace std;

Counter::Counter()
{
    
    
    cout << "init Counter" << endl;
}

Counter *Counter::instance()
{
    
    
	return &counter;
}

void Counter::plus(int number)
{
    
    
    counter.value += number;
}

void Counter::print()
{
    
    
    cout << counter.value << endl;
}

调用

#include "counter.h"

int main()
{
    
    
    cout << "program run" << endl;
    Counter::plus(5);
    Counter::print();
    
    return 0;
}

在这里插入图片描述

懒汉

懒汉模式是在第一次获取实例时调用。
同样是刚刚的例子。
counter.h

class Counter
{
    
    
public:
    static Counter* instance();
    void plus(int number);
    void print();

private:    //method
    Counter();

private:    //member
    int value{
    
     0 };
};

counter.cpp

#include "counter.h"
#include <iostream>

using namespace std;

Counter* Counter::instance()
{
    
    
    static Counter* counter = NULL;
    if (counter == NULL)
        counter = new Counter;
    return counter;
}

Counter::Counter()
{
    
    
    cout << "init Counter" << endl;
}

void Counter::plus(int number)
{
    
    
    value += number;
}

void Counter::print()
{
    
    
    cout << value << endl;
}

调用

#include "counter.h"

int main()
{
    
    
    cout << "program run" << endl;
    Counter::instance()->plus(5);
    Counter::instance()->print();
    
    return 0;
}

在这里插入图片描述

对比

通过上面的例子我们可以看到,饿汉模式和懒汉模式有以下区别。

函数调用

Counter::plus(5);
Counter::print();

Counter::instance()->plus(5);
Counter::instance()->print();

懒汉需要经过实例化函数instance()才能调用方法,饿汉不需要,显然饿汉模式更方便客户端的使用,且效率更高。

线程安全

懒汉是获取实例时才实例化,存在一个线程安全的问题:主线程和子线程同时调用instance()。显然这是不安全的,所以如果这个单例是多线程共享的,还需要加锁。

为了解决懒汉的线程安全问题,可以对代码做以下修改,这里用的锁是Qt的互斥锁。

Counter* Counter::instance()
{
    
    
	static QMutex mutex;
    static Counter* counter = NULL;
    if (counter == NULL) {
    
    
		mutex.lock();
		if (counter == NULL)
			counter = new Counter;
		mutex.unlock();
	}
    return counter;
}

这里先判断counter == NULL再加锁是为了避免每次进来都加锁导致性能损耗,虽然增加了代码篇幅,但是增加的部分被执行的次数很少,所以对性能影响可以忽略不计。

总结

通过以上对比,一般来说饿汉会比懒汉要好一些,但如果需要加载的单例很多,耗时比较长,这时候可以考虑使用懒汉。

猜你喜欢

转载自blog.csdn.net/weixin_45001971/article/details/130605954