单例模式,设计模式奥秘究竟隐藏何方?

2026-04-30 05:434阅读0评论工具资源
  • 内容介绍
  • 文章标签
  • 相关推荐

天哪, 写代码真的是一件让人抓狂的事情,特别是当你面对那些所谓的设计模式的时候。你有没有想过为什么我们要这么折磨自己?今天我们要聊的就是那个让人又爱又恨的——单例模式。说真的,这个模式简直就是设计模式里的“万金油”, 别怕... 哪里都能用,哪里都用不对。它的奥秘究竟隐藏何方?是隐藏在那些复杂的指针里还是隐藏在多线程的死锁中?我真的不知道,但我今天必须把它写出来不然我的头要炸了。

揭秘单例模式:解读设计模式的奥秘

什么是单例模式?基础中的基础

是不是? 先说说 我们得搞清楚,单例模式 Singleton就是保证一个类只有一个实例。听起来很简单对吧?就像你只能有一个老婆,虽然现实可能不是这样,但我们必须强制施行这个规则。我们要提供一个全局的访问点,让所有人都能找到这个唯一的对象。这就像是家里的钥匙,只有一把,谁要用谁就得去拿。

但是事情真的有这么简单吗?当然不是!如果你只是写个Hello World,那确实没问题。但是一旦涉及到多线程, 涉及到内存管理,涉及到各种乱七八糟的初始化顺序,这个单例模式就会变成一个噩梦。我就遇到过这种情况, 明明只想创建一个对象,后来啊程序崩了内存泄漏了甚至出现了多个实例,简直是奇耻大辱!

最简单的实现?也许吧

我们先来看一段代码。这段代码看起来很普通,但是它包含了很多陷阱。你看,这里有一个静态指针,还有一个GetInstance方法。如果实例不存在就new一个。听起来很完美?错!大错特错!

class Singleton {
public:
    static Singleton * GetInstance {
        if  {
            _instance = new Singleton;
            atexit;// 当程序退出时调用atexit里设置的Destructor函数
       }
        return _instance;
   }
private:
    static void Destructor {
        if  { //
            delete _instance;
            _instance = nullptr;
       }
   }
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
    static Singleton * _instance;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
// 还可以使⽤ 内部类,智能指针来解决;此时还有线程平安问题

看到了吗?这段代码虽然禁用了拷贝构造和赋值,甚至还有移动语义的删除,但是它有一个致命的弱点:线程不平安!如果有两个线程一边调用GetInstance, 而且刚好_instance都是nullptr,那么恭喜你,你创建了两个对象!这还叫什么单例?这叫双例!或者多例,琢磨琢磨。!

多线程的噩梦:锁与双重检测

为了解决上面那个愚蠢的错误,我们加锁。对,加锁!把互斥锁加上去,让线程排队。但是这样效率太低了每次获取实例都要加锁,简直是在浪费CPU的生命。于是聪明的人类发明了“双重检测锁定”。听起来很高大上,其实吧就是检查两次中间加锁,切中要害。。

#include 
class Singleton { // 懒汉模式 lazy load
public:
    static Singleton * GetInstance {
        // std::lock_guardlock; // 3.1 切换线程
        if  {
            std::lock_guardlock; // 3.2
            if  {
                _instance = new Singleton;
                // 1. 分配内存
                // 2. 调用构造函数
                // 3. 返回指针
                // 多线程环境下 cpu reorder操作
                atexit;
           }
       }
        return _instance;
   }
private:
    static void Destructor {
        if  {
            delete _instance;
            _instance = nullptr;
       }
   }
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
    static Singleton * _instance;
    static std::mutex _mutex;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化

但是!注意这个但是!即使你写了双重检测,你依然可能面临失败。为什么?主要原因是CPU会指令重排!new操作符在汇编中是有多个指令构成的, 它会做这些工作:分配内存、调用构造函数、 盘它... 返回指针;在多核环境下CPU会进行指令重排,这可能会造成程序奔溃。而这里的加锁,是从单线程的语义来的。这简直太可怕了你根本不知道你的CPU下一秒会干什么!

内存屏障与原子操作:终极武器?

为了彻底解决这个问题,我们需要祭出C++11的大杀器:原子变量和内存栅栏。C++ 98是单线程语义, C++11在C++98的基础上做了一些优化,编译器重排、CPU重排会违反顺序一致性。会产生可见性问题和施行序问题。为解决这些问题,C++ 11提供了同步原语:原子变量和内存栅栏,是吧?。

看这段代码,是不是觉得头昏脑涨?我也觉得。但是没办法,为了那个唯一的实例,我们必须这么做。

#include 
#include 
class Singleton{
public:
    static Singleton *GetInstance{
        Singleton *tmp=_instance.load;
        std::atomic_thread_fence;//获取内存屏障
        if
        {
            std::lock_guard lock;
            tmp=_instance.load;
            if{
                tmp= new Singleton;
                std::atomic_thread_fence;// 释放内存屏障
                _instance.store;
                atexit;
            }
        }
        return tmp;
    }
private:
    static void Destructor{
        Singleton* tmp=_instance.load;
        if
            delete tmp;
    }
    Singleton{};
    ~Singleton{};
    Singleton = delete;
    Singleton& operator= = delete;
    Singleton=delete;
    Singleton& operator==delete;
    static std::atomic _instance;
    static std::mutex _mutex;
};
std::atomic Singleton::_instance;//静态成员变量需要初始化
std::mutex Singleton::_mutex;//互斥锁初始化
// 编译
// g++  -o singleton -std=c++11

我晕... 使用原子变量,原子变量解决了三个问题:原子施行的问题。可见性问题。使用load和和store来解决。施行绪问题。使用内存模型解决, memory_order_acquire、memory_order_release。这简直是在挑战人类智商的极限!

各种单例实现方式大乱斗

也是没谁了。 为了让你更清楚, 我特意准备了一个表格,对比一下这几种让人抓狂的实现方式。看看哪个更适合你,或者说哪个更少让你掉头发。

实现方式 线程平安 性能 CPU指令重排风险 推荐指数
懒汉式 不平安
懒汉式 平安 ⭐⭐
双重检测锁定 基本平安 ⭐⭐⭐
C++11 Magic Static 平安 编译器保证 ⭐⭐⭐⭐⭐

看到了吗?C++11的Magic Static才是王道!但是为了学习,我们还是得把那些痛苦的过程走一遍,基本上...。

Java的单例模式:另一番景象

既然我们都在受苦,不如看看Java是怎么受苦的。Java 设计模式之单例模式 单例模式的三种实现方式 饿汉式 我破防了。 饿汉式顾铭思议是在类加载时便生成对象,由于在类加载的时候就生成了对象,饿汉式是线程平安的,但是会造成空间的浪费。

package com.lin.lon;
public class SingletonForHungry {
    public static SingletonForHungry singleton= new SingletonForHungry;
    private SingletonForHungry{
    }
    public static SingletonForHungry getInstance{
        return singleton;
    }
}

是不是? 然后是懒汉式。懒汉式相对于饿汉式而言,只有在方法被调用的时才会创建对象,由于懒汉式是动态的调用,所以呢会存在线程平安问题。第一种实现方式,该实现方式会造成线程平安问题。这和C++简直如出一辙,看来程序员无论用什么语言,面临的坑都是一样的。

实战中的混乱:O2O与框架

在写的过程中,发现现在框架中使用的YiiMongoDbSuite为模型类实现的一个单例模式很有意思,遂决定使用同样的写法实现我所需要的平台接入类.最近在做O2O平台的接入,主要原因是发现之前公司的代码里已经有了某家开放平台的接入代码,如果我再往原先的控制器上加入逻辑代码,整个控制器的耦合度会非常高。加上每个平台有自己的签名验证算法,把加解密的方法写到平台的接入控制器里固然好,但是还是有耦合...,我直接起飞。

一句话概括... 你看,这就是现实。理论是一回事,实际项目又是另一回事。你想要一个完美的单例后来啊项目里全是乱七八糟的代码,耦合度高得吓人。主要解决内存泄漏问题。_instance是静态全局分配的,程序退出自动释放。但是_instance是一个指针,指向一个堆,_instance虽然释放了但堆不自动释放。如果这个类是对文件操作的,那么程序退出时就无法关闭文件句柄和将未来得及写入文件的内容写入文件中。当然将_instance改用智能指针unique_ptr可以解决这个问题。

进阶:多态与友元类的黑魔法

解决变化点问题, 如果项目中有多个类都要写单例模式,通过继承的方式进行 。使用模板和友缘类。这听起来很酷, 功力不足。 对吧?我们不想为每个类都写一遍GetInstance,我们想要一个通用的模板。

搞起来。 添加多态。通过friend class让子类能够访问父类的private。这简直是打破规则的艺术!

template
class Singleton {
public:
    static T& GetInstance {
        static T instance; // 这⾥要初始化DesignPattern, 需要调⽤DesignPattern 构造函数,一边会调⽤⽗类的构造函数。
        return instance;
   }
protected:
    virtual ~Singleton {}
    Singleton {} // protected修饰构造函数,才能让别⼈继承
private:
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
};
class DesignPattern : public Singleton {
    //friend 能让Singleton 访问到 DesignPattern构造函数
    friend class Singleton;
private:
    DesignPattern {}
    ~DesignPattern {}
};

这段代码简直是神来之笔!通过模板和友元,我们实现了单例的复用。但是这也让代码变得极其复杂,维护起来简直是地狱。不过谁在乎呢?只要能跑就行,对吧?

Magic Static:救世主

差点意思。 再说说让我们来看看最推荐的写法。C++11出现的,有两种方式:函数l返回。该版本具备 版本4 所有优点。C++11 magic static 特性:如果当变量在初始化的时候, 并发一边进⼊声明语句,并发线程将会阻塞等待初始化结束。

// c++ effective
class Singleton{
public:
    static Singleton& GetInstance {
        static Singleton instance;
        return instance;
   }
private:
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
};
// 继承 Singleton
// g++  -o singleton -std=c++11

这才是真正的单例!简单、平安、高效。但是你真的理解它背后的原理吗? 我怀疑... 编译器到底帮我们做了什么?这又是一个深坑。

拷贝与移动:那些被遗忘的角落

我们一直在讨论如何创建单例却忘了如何防止它被复制。C++类的构造有:构造函数、拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。编写单例模式代码时需要考虑其线程平安性问题。同一个对象,它们是friend class的关系,可以互相访问私有成员,没眼看。。

两个对象之间赋值。这绝对是我们不希望看到的!

T t1;
T t2;

或者这样:

T t1;
T t2 = t1;

甚至这样:

T t1;
T t2{t1};

拷贝构造的触发有三个方式:直接用对象构造。如果不把这些都禁掉,你的单例随时可能被克隆出一堆分身。还有移动语义,改进一下。!

T t1;
T t2);

方式一:

T t1,t2;
t1=std::move;

方式二:

T t=CreateT;

注意, 如果没有禁掉返回值优化:-fno-elide-constructors;就会只有一个构造函数和一个析构函数,只进行一次构造和一次析构。如果禁掉返回值优化, 踩雷了。 编译器有三种行为:看类有没有移动构造;如果没有移动构造就看类有没有拷贝构造;如果两个都没有就会报错。这些细节简直让人发疯!

设计模式的奥秘

设计模式解决什么问题?本质上是分析稳定点和变化点。在开发过程中要抽象稳定的, 变化点。设计模式具体解决问题的场景:希望修改少量的代码,就可以适应需求的变化。设计模式的代码结构是什么?代码结构反映出使用了什么设计模式。设计模式符合哪些设计原则?主要原因是设计模式是由设计原则推导来的,可以按照设计模式产生的流程重新思考,能够很好的帮助我们去设计代码。如何在上面 代码?该设计模式有哪些典型应用场景,痛并快乐着。。

其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,一边提供能对该实例加以访问的全局访问方法。文章详细讲解了几种常见的设计模式,如单例模式和访问者模式,并提供了相关的代码实例供读者实践。.#设计模式#继承#多态java 基础一边被 2 个专栏收录16 篇文章,客观地说...。

这节我们就来认识一下几种单例模式的优劣。.简单单例模式主要缺点:在多线程施行同一个单利模式时,由于实例化过程的延迟,造成多线程实例化的先后顺序轮乱.提升效率。 我直接好家伙。 虽然加了锁,但是没有考虑到CPU的指令重排。

1.单例模式概述单例设计模式所解决的问题就是:保证类的对象在内存中唯一,即一个类只有一个对象实例 单例模式的结构单例类.java设计模式——单例模式.稳定点:类只有一个实例,提供一个全局的访问点。抽象稳定点。变化点:有多个类都是单例,能不能复用代码。 变化点,继承、组合的方式。

私有的构造和析构。单例模式和程序的生命周期是相同的,不希望new和delete的存在应用程序退出时单例模式才会释放。所以需要把构造函数和析构函数隐藏起来让用户不能调用。禁掉一些构造。把所有能构造的方式都关闭。比如 拷贝构造、拷贝赋值构造、移动构造、移动拷贝构造。静态类成员函数。静态私有成员变量,我CPU干烧了。。

1.单例模式 2.工厂方法模式 3.抽象工厂模式 4.模版方法模式 5.建造者模式 6.代理模式 7.原型模式 8.中介者模式.设计模式专栏收录该内容3 篇文章.关键在于掌握设计模式与遵循面向对象编程原则.

出岔子。 本文将深入探讨单例模式,它是最简单的设计模式之一,但其实现细节却值得我们细细品味.通过六个示例描述一步步完善单例模式的设计过程,需要考虑的问题。C++类的构造有:构造函数、拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。编写单例模式代码时需要考虑其线程平安性问题。同一个对象,它们是friend class的关系,可以互相访问私有成员。单例模式是很常见的设计模式,需要掌握。

为解决上述的问题,增加一个接口。解决多线程的线程平安问题。二.python实现单例模式.参考.1.基于new方法实现的单例模式.std::move。该版本具备 版本4 所有优点。 好吧好吧... C++11出现的,有两种方式:函数l返回。传入参数构造。其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,一边提供能对该实例加以访问的全局访问方法.

天哪, 写代码真的是一件让人抓狂的事情,特别是当你面对那些所谓的设计模式的时候。你有没有想过为什么我们要这么折磨自己?今天我们要聊的就是那个让人又爱又恨的——单例模式。说真的,这个模式简直就是设计模式里的“万金油”, 别怕... 哪里都能用,哪里都用不对。它的奥秘究竟隐藏何方?是隐藏在那些复杂的指针里还是隐藏在多线程的死锁中?我真的不知道,但我今天必须把它写出来不然我的头要炸了。

揭秘单例模式:解读设计模式的奥秘

什么是单例模式?基础中的基础

是不是? 先说说 我们得搞清楚,单例模式 Singleton就是保证一个类只有一个实例。听起来很简单对吧?就像你只能有一个老婆,虽然现实可能不是这样,但我们必须强制施行这个规则。我们要提供一个全局的访问点,让所有人都能找到这个唯一的对象。这就像是家里的钥匙,只有一把,谁要用谁就得去拿。

但是事情真的有这么简单吗?当然不是!如果你只是写个Hello World,那确实没问题。但是一旦涉及到多线程, 涉及到内存管理,涉及到各种乱七八糟的初始化顺序,这个单例模式就会变成一个噩梦。我就遇到过这种情况, 明明只想创建一个对象,后来啊程序崩了内存泄漏了甚至出现了多个实例,简直是奇耻大辱!

最简单的实现?也许吧

我们先来看一段代码。这段代码看起来很普通,但是它包含了很多陷阱。你看,这里有一个静态指针,还有一个GetInstance方法。如果实例不存在就new一个。听起来很完美?错!大错特错!

class Singleton {
public:
    static Singleton * GetInstance {
        if  {
            _instance = new Singleton;
            atexit;// 当程序退出时调用atexit里设置的Destructor函数
       }
        return _instance;
   }
private:
    static void Destructor {
        if  { //
            delete _instance;
            _instance = nullptr;
       }
   }
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
    static Singleton * _instance;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
// 还可以使⽤ 内部类,智能指针来解决;此时还有线程平安问题

看到了吗?这段代码虽然禁用了拷贝构造和赋值,甚至还有移动语义的删除,但是它有一个致命的弱点:线程不平安!如果有两个线程一边调用GetInstance, 而且刚好_instance都是nullptr,那么恭喜你,你创建了两个对象!这还叫什么单例?这叫双例!或者多例,琢磨琢磨。!

多线程的噩梦:锁与双重检测

为了解决上面那个愚蠢的错误,我们加锁。对,加锁!把互斥锁加上去,让线程排队。但是这样效率太低了每次获取实例都要加锁,简直是在浪费CPU的生命。于是聪明的人类发明了“双重检测锁定”。听起来很高大上,其实吧就是检查两次中间加锁,切中要害。。

#include 
class Singleton { // 懒汉模式 lazy load
public:
    static Singleton * GetInstance {
        // std::lock_guardlock; // 3.1 切换线程
        if  {
            std::lock_guardlock; // 3.2
            if  {
                _instance = new Singleton;
                // 1. 分配内存
                // 2. 调用构造函数
                // 3. 返回指针
                // 多线程环境下 cpu reorder操作
                atexit;
           }
       }
        return _instance;
   }
private:
    static void Destructor {
        if  {
            delete _instance;
            _instance = nullptr;
       }
   }
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
    static Singleton * _instance;
    static std::mutex _mutex;
};
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化

但是!注意这个但是!即使你写了双重检测,你依然可能面临失败。为什么?主要原因是CPU会指令重排!new操作符在汇编中是有多个指令构成的, 它会做这些工作:分配内存、调用构造函数、 盘它... 返回指针;在多核环境下CPU会进行指令重排,这可能会造成程序奔溃。而这里的加锁,是从单线程的语义来的。这简直太可怕了你根本不知道你的CPU下一秒会干什么!

内存屏障与原子操作:终极武器?

为了彻底解决这个问题,我们需要祭出C++11的大杀器:原子变量和内存栅栏。C++ 98是单线程语义, C++11在C++98的基础上做了一些优化,编译器重排、CPU重排会违反顺序一致性。会产生可见性问题和施行序问题。为解决这些问题,C++ 11提供了同步原语:原子变量和内存栅栏,是吧?。

看这段代码,是不是觉得头昏脑涨?我也觉得。但是没办法,为了那个唯一的实例,我们必须这么做。

#include 
#include 
class Singleton{
public:
    static Singleton *GetInstance{
        Singleton *tmp=_instance.load;
        std::atomic_thread_fence;//获取内存屏障
        if
        {
            std::lock_guard lock;
            tmp=_instance.load;
            if{
                tmp= new Singleton;
                std::atomic_thread_fence;// 释放内存屏障
                _instance.store;
                atexit;
            }
        }
        return tmp;
    }
private:
    static void Destructor{
        Singleton* tmp=_instance.load;
        if
            delete tmp;
    }
    Singleton{};
    ~Singleton{};
    Singleton = delete;
    Singleton& operator= = delete;
    Singleton=delete;
    Singleton& operator==delete;
    static std::atomic _instance;
    static std::mutex _mutex;
};
std::atomic Singleton::_instance;//静态成员变量需要初始化
std::mutex Singleton::_mutex;//互斥锁初始化
// 编译
// g++  -o singleton -std=c++11

我晕... 使用原子变量,原子变量解决了三个问题:原子施行的问题。可见性问题。使用load和和store来解决。施行绪问题。使用内存模型解决, memory_order_acquire、memory_order_release。这简直是在挑战人类智商的极限!

各种单例实现方式大乱斗

也是没谁了。 为了让你更清楚, 我特意准备了一个表格,对比一下这几种让人抓狂的实现方式。看看哪个更适合你,或者说哪个更少让你掉头发。

实现方式 线程平安 性能 CPU指令重排风险 推荐指数
懒汉式 不平安
懒汉式 平安 ⭐⭐
双重检测锁定 基本平安 ⭐⭐⭐
C++11 Magic Static 平安 编译器保证 ⭐⭐⭐⭐⭐

看到了吗?C++11的Magic Static才是王道!但是为了学习,我们还是得把那些痛苦的过程走一遍,基本上...。

Java的单例模式:另一番景象

既然我们都在受苦,不如看看Java是怎么受苦的。Java 设计模式之单例模式 单例模式的三种实现方式 饿汉式 我破防了。 饿汉式顾铭思议是在类加载时便生成对象,由于在类加载的时候就生成了对象,饿汉式是线程平安的,但是会造成空间的浪费。

package com.lin.lon;
public class SingletonForHungry {
    public static SingletonForHungry singleton= new SingletonForHungry;
    private SingletonForHungry{
    }
    public static SingletonForHungry getInstance{
        return singleton;
    }
}

是不是? 然后是懒汉式。懒汉式相对于饿汉式而言,只有在方法被调用的时才会创建对象,由于懒汉式是动态的调用,所以呢会存在线程平安问题。第一种实现方式,该实现方式会造成线程平安问题。这和C++简直如出一辙,看来程序员无论用什么语言,面临的坑都是一样的。

实战中的混乱:O2O与框架

在写的过程中,发现现在框架中使用的YiiMongoDbSuite为模型类实现的一个单例模式很有意思,遂决定使用同样的写法实现我所需要的平台接入类.最近在做O2O平台的接入,主要原因是发现之前公司的代码里已经有了某家开放平台的接入代码,如果我再往原先的控制器上加入逻辑代码,整个控制器的耦合度会非常高。加上每个平台有自己的签名验证算法,把加解密的方法写到平台的接入控制器里固然好,但是还是有耦合...,我直接起飞。

一句话概括... 你看,这就是现实。理论是一回事,实际项目又是另一回事。你想要一个完美的单例后来啊项目里全是乱七八糟的代码,耦合度高得吓人。主要解决内存泄漏问题。_instance是静态全局分配的,程序退出自动释放。但是_instance是一个指针,指向一个堆,_instance虽然释放了但堆不自动释放。如果这个类是对文件操作的,那么程序退出时就无法关闭文件句柄和将未来得及写入文件的内容写入文件中。当然将_instance改用智能指针unique_ptr可以解决这个问题。

进阶:多态与友元类的黑魔法

解决变化点问题, 如果项目中有多个类都要写单例模式,通过继承的方式进行 。使用模板和友缘类。这听起来很酷, 功力不足。 对吧?我们不想为每个类都写一遍GetInstance,我们想要一个通用的模板。

搞起来。 添加多态。通过friend class让子类能够访问父类的private。这简直是打破规则的艺术!

template
class Singleton {
public:
    static T& GetInstance {
        static T instance; // 这⾥要初始化DesignPattern, 需要调⽤DesignPattern 构造函数,一边会调⽤⽗类的构造函数。
        return instance;
   }
protected:
    virtual ~Singleton {}
    Singleton {} // protected修饰构造函数,才能让别⼈继承
private:
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
};
class DesignPattern : public Singleton {
    //friend 能让Singleton 访问到 DesignPattern构造函数
    friend class Singleton;
private:
    DesignPattern {}
    ~DesignPattern {}
};

这段代码简直是神来之笔!通过模板和友元,我们实现了单例的复用。但是这也让代码变得极其复杂,维护起来简直是地狱。不过谁在乎呢?只要能跑就行,对吧?

Magic Static:救世主

差点意思。 再说说让我们来看看最推荐的写法。C++11出现的,有两种方式:函数l返回。该版本具备 版本4 所有优点。C++11 magic static 特性:如果当变量在初始化的时候, 并发一边进⼊声明语句,并发线程将会阻塞等待初始化结束。

// c++ effective
class Singleton{
public:
    static Singleton& GetInstance {
        static Singleton instance;
        return instance;
   }
private:
    Singleton{}; //构造
    ~Singleton{};
    Singleton = delete; //拷⻉构造
    Singleton& operator= =delete;//拷贝赋值构造
    Singleton = delete;//移动构造
    Singleton& operator= =delete;//移动拷贝构造
};
// 继承 Singleton
// g++  -o singleton -std=c++11

这才是真正的单例!简单、平安、高效。但是你真的理解它背后的原理吗? 我怀疑... 编译器到底帮我们做了什么?这又是一个深坑。

拷贝与移动:那些被遗忘的角落

我们一直在讨论如何创建单例却忘了如何防止它被复制。C++类的构造有:构造函数、拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。编写单例模式代码时需要考虑其线程平安性问题。同一个对象,它们是friend class的关系,可以互相访问私有成员,没眼看。。

两个对象之间赋值。这绝对是我们不希望看到的!

T t1;
T t2;

或者这样:

T t1;
T t2 = t1;

甚至这样:

T t1;
T t2{t1};

拷贝构造的触发有三个方式:直接用对象构造。如果不把这些都禁掉,你的单例随时可能被克隆出一堆分身。还有移动语义,改进一下。!

T t1;
T t2);

方式一:

T t1,t2;
t1=std::move;

方式二:

T t=CreateT;

注意, 如果没有禁掉返回值优化:-fno-elide-constructors;就会只有一个构造函数和一个析构函数,只进行一次构造和一次析构。如果禁掉返回值优化, 踩雷了。 编译器有三种行为:看类有没有移动构造;如果没有移动构造就看类有没有拷贝构造;如果两个都没有就会报错。这些细节简直让人发疯!

设计模式的奥秘

设计模式解决什么问题?本质上是分析稳定点和变化点。在开发过程中要抽象稳定的, 变化点。设计模式具体解决问题的场景:希望修改少量的代码,就可以适应需求的变化。设计模式的代码结构是什么?代码结构反映出使用了什么设计模式。设计模式符合哪些设计原则?主要原因是设计模式是由设计原则推导来的,可以按照设计模式产生的流程重新思考,能够很好的帮助我们去设计代码。如何在上面 代码?该设计模式有哪些典型应用场景,痛并快乐着。。

其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,一边提供能对该实例加以访问的全局访问方法。文章详细讲解了几种常见的设计模式,如单例模式和访问者模式,并提供了相关的代码实例供读者实践。.#设计模式#继承#多态java 基础一边被 2 个专栏收录16 篇文章,客观地说...。

这节我们就来认识一下几种单例模式的优劣。.简单单例模式主要缺点:在多线程施行同一个单利模式时,由于实例化过程的延迟,造成多线程实例化的先后顺序轮乱.提升效率。 我直接好家伙。 虽然加了锁,但是没有考虑到CPU的指令重排。

1.单例模式概述单例设计模式所解决的问题就是:保证类的对象在内存中唯一,即一个类只有一个对象实例 单例模式的结构单例类.java设计模式——单例模式.稳定点:类只有一个实例,提供一个全局的访问点。抽象稳定点。变化点:有多个类都是单例,能不能复用代码。 变化点,继承、组合的方式。

私有的构造和析构。单例模式和程序的生命周期是相同的,不希望new和delete的存在应用程序退出时单例模式才会释放。所以需要把构造函数和析构函数隐藏起来让用户不能调用。禁掉一些构造。把所有能构造的方式都关闭。比如 拷贝构造、拷贝赋值构造、移动构造、移动拷贝构造。静态类成员函数。静态私有成员变量,我CPU干烧了。。

1.单例模式 2.工厂方法模式 3.抽象工厂模式 4.模版方法模式 5.建造者模式 6.代理模式 7.原型模式 8.中介者模式.设计模式专栏收录该内容3 篇文章.关键在于掌握设计模式与遵循面向对象编程原则.

出岔子。 本文将深入探讨单例模式,它是最简单的设计模式之一,但其实现细节却值得我们细细品味.通过六个示例描述一步步完善单例模式的设计过程,需要考虑的问题。C++类的构造有:构造函数、拷贝构造、拷贝赋值构造、移动构造、移动赋值构造。编写单例模式代码时需要考虑其线程平安性问题。同一个对象,它们是friend class的关系,可以互相访问私有成员。单例模式是很常见的设计模式,需要掌握。

为解决上述的问题,增加一个接口。解决多线程的线程平安问题。二.python实现单例模式.参考.1.基于new方法实现的单例模式.std::move。该版本具备 版本4 所有优点。 好吧好吧... C++11出现的,有两种方式:函数l返回。传入参数构造。其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,一边提供能对该实例加以访问的全局访问方法.