C++单例模式

1、单例模式

        单例模式是C++的一种设计模式,其核心思想是确保一个类中只有一个实例,也就是说一个类中仅有一个对象,且能被其他变量访问。相当于创建了一个全局变量。

2、使用场景

        (1)资源共享:例如log/配置文件等在整个程序中都需要使用,使用单例模式可以确保这些资源都仅有一个实例,避免资源浪费。

        (2)数据:用来缓存数据的数据结构,需要在一处写,多处读取/多处写,多处读取。

        (3)单元测试:可以用来创建单元类,搭建单元测试框架。

3、定义

        (1)私有化构造函数:防止外界创建单例类的对象,不需要赋值。

        (2)静态成员变量:提供一个自身的静态私有成员变量,以指向类的实例。

        (3)静态方法:提供一个公有的静态成员函数,用来返回这个唯一实例。

4、实现

         (1)模式一:“懒汉模式”

                       <1>特点: 在第一次调用getInstance()方法时才创建单例对象。只适用于单线程,如果多个线程同时调用getInstance(),可能会同时创建多个单例对象,从而不安全。因此多线程中需要注意线程安全,最好加锁防止冲突。

                        <2>代码示例:

#include <iostream>
#include <unistd.h>

class Singleton
{
private:
    Singleton() // 构造函数与析构函数的私有化
    {
        std::cout << "懒汉模式" << "\n";
    }
    ~Singleton() {}

    Singleton(const Singleton &) = delete;            // 禁止拷贝
    Singleton &operator=(const Singleton &) = delete; // 禁止赋值

    static Singleton *data;       // 使用Singleton*作为访问唯一对象的入口
public:
    static Singleton *GetInstance() // 公有的静态方法,外部只能通过该函数获取data
    {
        if (data == nullptr)
        {
            if (data == nullptr)
                data = new Singleton(); // 需要时才创建实例
        }

        return data;
    }
};

Singleton *Singleton::data = nullptr;                         // data的初始化

int main()
{
    // 只有调用GetInstance时,才会开辟空间
    std::cout << Singleton::GetInstance() << "\n";
    return 0;
}

        运行结果:构造函数中的打印和data的地址

        (2)模式二:“饿汉模式”

                          <1>特点: 在类加载时就立即创建单例对象,并将其存储在静态成员变量中。这种模式相比较“懒汉模式”而言,更适合多线程,由于一开始就创建好对象,所以在多线程环境中不会出现多个线程同时创建对象的情况。但可能会出现资源浪费的情形。

                        <2>代码示例:

#include <iostream>
#include <unistd.h>

class Singleton
{
private:
    Singleton() // 构造函数与析构函数的私有化
    {
        std::cout << "饿汉模式" << "\n";
    }
    ~Singleton() {}

    Singleton(const Singleton &) = delete;            // 禁止拷贝
    Singleton &operator=(const Singleton &) = delete; //禁止赋值

    static Singleton data; // 静态实例,程序启动时创建

public:
    static Singleton *GetInstance() //公有的静态方法,外部只能通过该函数获取data
    {
        return &data;
    }
};

Singleton Singleton::data; // data的初始化,必须在类外。


int main()
{
    // 以下两种实例化方式都无法实例化出对象
    //  Singleton s;
    //  Singleton s1(s);

    std::cout << Singleton::GetInstance() << "\n";
    return 0;
}

运行结果:构造函数中的打印和data的地址

5、进阶使用

        (1)融入模板的单例模式

        代码示例:

#include <iostream>

using namespace std;

template <typename T>
class Singleton
{
public:
    static T &Getinstance() // 外界可访问的唯一接口
    {
        Init();
        return *instance;
    }

private:
    static void Init()
    {
        if (instance == 0)
        {
            instance = new T;        // 初始化实例
            atexit(DestroyInstance); // 当程序结束时析构
        }
    }
    static void DestroyInstance()
    {
        delete instance;
        instance = nullptr;
    }

    Singleton();
    ~Singleton();
    Singleton(const Singleton<T> &) {}
    Singleton<T> &operator=(const Singleton<T> &) {}

    static T *instance; // 私有的静态指针,指向唯一的实例,也可以用智能指针,定义为智能指针时不需要手动释放 static std::shared_ptr<T>...
};
template <typename T>
T *Singleton<T>::instance = nullptr; // 初始化

// example
class A
{
public:
    A()
    {
        std::cout << "begin...." << "\n";
    }
    ~A()
    {
        std::cout << "end...." << "\n";
    }
    void Run()
    {
        std::cout << "run...." << "\n";
    }
};
typedef Singleton<A> a;

int main()
{
    a::Getinstance().Run();
    // Singleton<A>::Getinstance().Run(); //两种方法均可,
    return 0;
}

        使用的时候最好在初始化实例的时候即 instance = new T 的前后加上锁,防止冲突。

        (2)使用模板单例,添加友元函数

                日常的使用习惯会将上述单例类模板写为头文件,当我们需要这个单例类模板时,只需要在自己类里通过friend添加为友元即可。下方头文件不再赘述,具体描述应用程序的编写

        代码示例:

#include"Singleton.h"

using namespace std;

class A
{
    friend class Singleton<A>;  //声明A的友元为单例类模板
public:
    A()
    {
        std::cout << "begin...." << "\n";
    }
    ~A()
    {
        std::cout << "end...." << "\n";
    }
    void Run()
    {
        std::cout << "run...." << "\n";
    }
};
typedef Singleton<A> a;

int main()
{
    a::Getinstance().Run();
    // Singleton<A>::Getinstance().Run();
}

        (3)使用可变参数模板-优化单例模式

        头文件:

#pragma once
#ifndef _SINGLETON_H_
#define _SINGLETON_H_
#include <iostream>

using namespace std;

template <typename T>
class Singleton
{
public:
    template<typename...Args>
    static T &Getinstance(Args&&...args) // 外界可访问的唯一接口
    {
        if (instance == nullptr)
        {
            instance = new T(std::forward<Args>(args)...);        // 初始化实例
            atexit(DestroyInstance); // 当程序结束时析构
        }
        return *instance;
    }

private:
    static void DestroyInstance()
    {
        delete instance;
        instance = nullptr;
    }

    Singleton();
    ~Singleton();
    Singleton(const Singleton<T> &) {}
    Singleton<T> &operator=(const Singleton<T> &) {}

    static T *instance; // 私有的静态指针,指向唯一的实例,也可以用智能指针,定义为智能指针时不需要手动释放 static std::shared_ptr<T>...
};
template <class T>
T *Singleton<T>::instance = nullptr; // 初始化
#endif

程序:

#include "Singleton.h"

using namespace std;

struct A
{
    friend class Singleton<A>;
    A(const string &)
    {
        cout << "A1" << "\n";
    }
    A(string &&x)
    {
        cout << "A2" << "\n";
    }
};
int main()
{
    string str = "a";
    Singleton<A>::Getinstance(str);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值