单例模式

本文详细探讨了单例模式的应用实例、优缺点以及实现方式,包括懒汉模式、Meyers模式,并通过Qt中的全局指针QApplication和QCoreApplication来解释单例模式在实际中的运用。文中指出,虽然单例模式常常限制构造和析构为private,但Qt的实现方式提供了另一种思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用实例: 1、日志类,一个应用往往只对应一个日志实例。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。3.windows系统的任务管理器就是一个例子,总是只有一个管理器的实例。

优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

一个单例模式应具备以下特征:
1. 只能实例化同一个对象
2. 提供全局访问点
3. 禁止拷贝

针对第一条,可以将构造函数权限设为private,如果是public那么每次实例化调用构造函数,对象的内存地址都不同,也就是说只准调用一次构造函数。那问题来了,构造函数都private了,怎么调用?显然要用某个public方法来调用,又有问题:都没实例化对象,怎么调用public方法?那这个public方法只能是静态的了。

针对第二条,全局性很容易想到静态函数,它是属于类的,而不是属于某个对象的。

第三条很容易,将拷贝构造函数和复制运算符声明为private即可。

综上,单例类的雏形应该是这样的:

class Singleton
{
public:
    // 单例方法

private:    //构造函数或析构函数为私有函数,所以该类是无法被继承的,
    Singleton(){std::cout<<"单例构造"<<endl; }
    ~Singleton(){std::cout<<"单例析构"<<endl;  }
    Singleton(const Singleton &);
    Singleton& operator=(const Singleton&);
    static Singleton* m;
};

关键就是怎么实现public static方法。

测试1

首先想到这样的方法:

static Singleton instance()
{
    Singleton s;
    return s;
}

结果报错构造函数和析构函数是private

测试2

测试下面这种方法

static Singleton* instance1()
{
    Singleton *s = new Singleton();
    return s;
}
    Singleton* s1 = Singleton::instance1();
    Singleton *s2 = Singleton::instance1();
    delete s1;
    delete s2;

结果发现有两个构造函数,而且无法析构。

懒汉模式

class Singleton
{
public:
    static Singleton* LazyInstance()
    {
        if(!m)
            m = new Singleton();
        return m;
    }
private:
    Singleton(){std::cout<<"单例构造"<<endl; }
    ~Singleton(){std::cout<<"单例析构"<<endl;  }
    Singleton(const Singleton &);
    Singleton& operator=(const Singleton&);
    static Singleton* m;
};

测试:

    Singleton* s = Singleton::LazyInstance();
    std::cout<<s<<endl;
    Singleton* s2 = s;
    std::cout<<s2<<endl;
    delete s;
    delete s2;

结果发现只调用一次构造函数,s和s2的内存地址相同,但析构时报错无法访问Private成员,也就是说这种方式无法析构,有内存泄漏的危险。可以补充一个public函数专门析构,但这样太繁琐,不推荐:

void destructor()   //补充懒汉模式的析构
{
    if(m)
    {
        delete m;
        m = NULL;
    }
    return;
}

另外在多线程情况下,如果两个线程同时调用这个函数,判断m为空指针时,两个线程会同时执行函数,这就不符合单一实例的要求。

Meyers模式

static Singleton& MeyersInstance()
{
    static Singleton s;
    return s;
}

使用局部静态变量实现,当第一次访问Instance()方法时才创建实例。

在 MeyersInstance() 函数内定义局部静态变量的好处是,构造函数只会在第一次调用MeyersInstance() 时被初始化, 保证了成员变量和 Singleton 本身的初始化顺序。
它还有一个潜在的安全措施, MeyersInstance() 返回的是对局部静态变量的引用, 如果返回的是指针, MeyersInstance() 的调用者很可能会误认为他要检查指针的有效性, 并负责销毁。

Qt中的全局指针

Qt里有一个全局指针qApp,在任意地方都能使用,看看是不是单例模式。

QApplication中:

#define qApp (static_cast<QApplication *>
(QCoreApplication::instance()))

QCoreapplication中:

//头文件中
#define  qApp  QCoreApplication::instance()

static QCoreApplication  *instance() { return self; }

static QCoreApplication  *self;

//源文件中
QCoreApplication *QCoreApplication::self = 0;
void QCoreApplicationPrivate::init()
{
    ......
    Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
    QCoreApplication::self = q;
    ......
}

QCoreApplication::QCoreApplication
{
    d_func()->q_ptr = this;
    d_func()->init();
    QCoreApplicationPrivate::eventDispatcher->startingUp();
}

从QCoreapplication来看,qApp是个宏,实际是函数QCoreApplication::instance()。从这个self来看,特别像懒汉模式,不过其构造函数和析构函数不是private,self是在QCoreApplication构造函数里赋值,赋给它的q指针实际就是QCoreApplication的this指针。

但是在程序里使用qApp,你会发现其地址都一样,也就是同一个全局指针,这就在于Q_ASSERT_X这句限定了只能有一个QCoreApplication对象,再加上拷贝构造函数和赋值运算符都在QObject限定为private,因此qApp也是一种单例模式。所以我们可以说单例模式不一定限定构造和析构是private。

参考:探究 C++ Singleton(单例模式)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值