C++中的单例模式的实现

发布于:2025-09-11 ⋅ 阅读:(24) ⋅ 点赞:(0)

1 什么是单例模式

单例模式 是一种创建型设计模式,确保一个类在整个程序生命周期中只有一个实例,并提供一个全局访问点。

核心要求:

  1. 类不能被外部随意创建(禁止 public 构造函数或限制实例数量)。
  2. 不能被复制或移动。
  3. 提供一个全局访问接口(如 getInstance())。
  4. 实例的生命周期通常伴随整个程序。

2 模版单例的实现

2.1 单例模版GSingleton<T>

  • 使用场景:项目中需要大量单例,且希望统一管理时,使用单例模版会提高代码复用。单例模版类本身不能直接使用,而是作为基类被继承。需要实现单例的类只需要继承GSingleton即可。

  • 注意:模版基类GSingleton的构造函数和析构函数必须是protected修饰,不能用private,因为派生类在构造时或者析构时可以调用。而派生类T的构造函数和析构函数可以由private修饰。

template<typename T>
class GSingleton
{
protected:
    GSingleton() = default;
    ~GSingleton() = default;  // 非虚,除非你真的需要

    // 禁止拷贝和移动
    GSingleton(const GSingleton&) = delete;
    GSingleton& operator=(const GSingleton&) = delete;
    GSingleton(GSingleton&&) = delete;
    GSingleton& operator=(GSingleton&&) = delete;

public:
    static T& getInstance() {
        static T instance;
        return instance;
    }
};
  • 模版类详细介绍
  1. protect:修饰的方法或者成员变量只能被本类、派生类以及友元访问。
  2. Gsingleton<T>是基类,必须是protected,这样派生类在构造时才可以调用父类的构造函数。
  3. GSingleton = default:表示显式要求编译器生成一个默认构造函数(无参构造函数)
  4. ~GSingleton() = default:表示让编译器生成默认析构函数。
  5. GSingleton(const GSingleton&) = delete:这是对拷贝构造函数的声明,禁止拷贝。
    const GSingleton&:表示对另一个GSingleton对象的常量引用。
    delete表示这个函数被显示删除,不能使用。
    Access a;
    Access b(a);        // ❌ 错误!调用拷贝构造函数
    Access c = a;       // ❌ 错误!也是拷贝构造(赋值语法,实际是构造)
    
  6. GSingleton& operator=(const GSingleton&) = delete:这是对拷贝赋值运算符的声明,禁止拷贝。
    operator=:表示赋值操作符的重载。
    Access a;
    Access b;
    b = a;              // ❌ 错误!调用拷贝赋值
  7. GSingleton(GSingleton&&) = delete:这是移动构造函数的声明,禁止移动构造。
    GSingleton&&:是一个右值引用,表示临时对象或者可移动的对象。
    移动不是拷贝,而是资源转移,将一个临时对象所拥有的资源转移给另一个对象,而原对象不再拥有该资源。
    移动避免了不必要的内存分配和数据赋值,因此非常高效。
    Access a;
    Access b(std::move(a));  // ❌ 错误!试图移动构造(即使 NRVO 优化,语义上也不允许)
  8. GSingleton& operator=(GSingleton&&) = delete:这是移动赋值运算符,禁止将一个临时对象移动赋值给已有对象。
    Access a;
    Access b;
    b = std::move(a);   // ❌ 错误!禁止移动赋值

✅ 总结

代码 禁止的操作
Access(const Access&) = delete; Access b(a); 或 Access b = a;
Access& operator=(const Access&) = delete; b = a;
Access(Access&&) = delete; Access b(std::move(a));
Access& operator=(Access&&) = delete; b = std::move(a);

2.2 使用单例模版定义派生类

class GLoggerImpl : public GSingleton<GLoggerImpl>
{
private:
	GLoggerImpl();
	friend class GSingleton<GLoggerImpl>;
public:
	~GLoggerImpl();
    void WriteLog(int logLevel, const char* file, int line, const char* function, const char* format, va_list args);
}

注意:在声明GloggerImpl的类时使用了friend class GSingleton<GLoggerImpl>,表示声明让模版类成为友元。

原因:在调用GLoggerImpl::getInstance()时,

// template<typename T> class GSingleton 
static T& getInstance() {
    static T instance;  // ← 这里要构造 T,即 GLoggerImpl
    return instance;
}

static T instance会调用GLoggerImpl的构造函数,由于GLoggerImpl()是private或者protected,而GSingleton<GLoggerImpl>模版类不是GLoggerImpl类的成员变量,默认无法访问GLoggerImpl的构造函数,因此要声明friend来显示授权。

友元:是C++的一个关键字,它允许一个类、函数或者模版访问另一个类的private和protected成员,即使它不是那个类的成员函数或者派生类。

  • friend可以修饰函数,称为友元函数,可以在一个类中声明友元函数,这样该函数可以访问该类的私有成员变量或者成员函数。
  • friend可以修饰类,成为友元类,可以访问所在类中的private或者protect的成员,友元关系是单向的。
  • friend可以修饰一个模版类或者模版函数,成为友元模版。例如GloggerImpl中friend修饰的模版。

2.3 单例类的使用

由于GLoggerImpl继承了GSingleton的模版基类,因此继承了父类静态的getInstance()方法。所以通过GLoggerImpl::getInstance()可以获取该类唯一的单例对象。

void LogD(const char* file, int line, const char* function, const char* format, ...) {
	va_list args;
	va_start(args, format);
	GLoggerImpl::getInstance()->WriteLog(LEVEL_DEBUG, file, line, function, format, args);
	va_end(args);
}

3 单例类的实现

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;  // C++11 起,线程安全
        return instance;
    }

    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

private:
    Singleton() = default;
    ~Singleton() = default;
};

 

 


网站公告

今日签到

点亮在社区的每一天
去签到