设计模式面试重点

发布时间 2023-10-12 17:03:24作者: 游客0721

mid

单例模式

说说什么是单例设计模式,如何实现

1.单例模式定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

前提

  • 该类不能被复制。
  • 该类不能被公开的创造

那么对于C++来说,它的构造函数,拷贝构造函数和赋值函数都不能被公开调用。

2.单例模式实现方式

单例模式通常有两种模式,分别为懒汉式单例和饿汉式单例。两种模式实现方式分别如下

懒汉式设计模式实现方式(2种)

  • 静态指针 + 用到时初始化

  • 局部静态变量

饿汉式设计模式(2种)

  • 直接定义静态对象

  • 静态指针 + 类外初始化时new空间实现

3.具体解析

1.懒汉模式: 懒汉模式的特点是延迟加载,比如配置文件,采用懒汉式的方法,配置文件的实例直到用到的时候才会加载,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化。以下是懒汉模式实现方式

懒汉模式实现一:静态指针 + 用到时初始化
template<typename T>
class Singleton {
public:
    static T& getInstance() {
        if (!value_) {
            value_ = new T();
        }
        return *value_;
    }

private:
    Singleton();
    ~Singleton();
    static T* value_;
};

template<typename T>
T* Singleton<T>::value_ = nullptr;

在单线程环境中,上述代码可以正常工作。然而,在多线程环境中,这种实现方式是线程不安全的。下面是对多线程环境下可能发生的问题的分段解释:

  • 假设线程A和线程B都要访问 getInstance 函数。线程A进入函数并检查 if 条件,由于是第一次进入,value 为空,if 条件成立,准备创建对象实例

  • 然而,线程A可能在创建对象实例之前被操作系统的调度器中断,并被挂起(睡眠),将控制权交给线程B。

  • 线程B同样进入 if 条件,发现 value 仍然为 NULL,因为线程A还没有来得及构造它就被中断了。线程B完成对象的创建并成功返回。

  • 之后,线程A被唤醒并继续执行 new 来再次创建对象。这样一来,两个线程都构建了自己的对象实例,破坏了单例模式的唯一性。

除了线程安全问题,该实现还存在内存泄漏的问题。通过 new 创建的对象始终没有被释放。

改进
template<typename T>
class Singleton {
public:
    static T& getInstance() {
        if (!value_) {
            static CGarbo garbo; // 定义一个静态局部变量,利用静态局部变量的特性确保线程安全
            value_ = new T();
        }
        return *value_;
    }

private:
    class CGarbo {
    public:
        ~CGarbo() {
            if (Singleton::value_) {
                delete Singleton::value_;
            }
        }
    };

    Singleton();
    ~Singleton();
    static T* value_;
};

template<typename T>
T* Singleton<T>::value_ = nullptr;

在程序运行结束时,系统会调用Singleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。使用这种方法释放单例对象有以下特征:

  • 在单例类内部定义专有的嵌套类

  • 在单例类内定义私有的专门用于释放的静态成员

  • 利用程序在结束时析构全局变量的特性,选择最终的释放时机

懒汉模式实现二:局部静态变量 (略)

如果存在多个单例对象且这几个单例对象相互依赖,可能会出现程序崩溃的危险。原因:对编译器来说,静态成员变量的初始化顺序和析构顺序是一个未定义的行为

2.饿汉模式

单例类定义的时候就进行实例化。因为main函数执行之前,全局作用域的类成员静态变量m_Instance已经初始化,故没有多线程的问题。

饿汉模式实现一:直接定义静态对象
// .h文件
class Singleton {
public:
    static Singleton& GetInstance();

private:
    Singleton() {}
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

private:
    static Singleton m_Instance;
};

// CPP文件
Singleton Singleton::m_Instance; // 类外定义-不要忘记写

Singleton& Singleton::GetInstance() {
    return m_Instance;
}

// 函数调用
Singleton& instance = Singleton::GetInstance();

缺点

在程序开始时,就创建类的实例,如果Singleton对象产生很昂贵,而本身有很少使用,这种方式单从资源利用效率的角度来讲,比懒汉式单例类稍差些。但从反应时间角度来讲,则比懒汉式单例类稍好些。

请说说工厂设计模式,如何实现,以及它的优点

1.工厂设计模式的定义

定义一个创建对象的接口,让子类决定实例化哪个类,而对象的创建统一交由工厂去生产,有良好的封装性,既做到了解耦

2.工厂设计模式分类

工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式

1.简单工厂模式

它的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。

举例:有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显示地告诉生产工厂。

点击查看代码
//程序实例(简单工厂模式)
enum CTYPE {COREA, COREB};     
class SingleCore    
{    
public:    
    virtual void Show() = 0;  
};    
//单核A    
class SingleCoreA: public SingleCore    
{    
public:    
    void Show() { cout<<"SingleCore A"<<endl; }    
};    
//单核B    
class SingleCoreB: public SingleCore    
{    
public:    
    void Show() { cout<<"SingleCore B"<<endl; }    
};    
//唯一的工厂,可以生产两种型号的处理器核,在内部判断    
class Factory    
{    
public:     
    SingleCore* CreateSingleCore(enum CTYPE ctype)    
    {    
        if(ctype == COREA) //工厂内部判断    
            return new SingleCoreA(); //生产核A    
        else if(ctype == COREB)    
            return new SingleCoreB(); //生产核B    
        else    
            return NULL;    
    }    
};    

优点: 简单工厂模式可以根据需求,动态生成使用者所需类的对象,而使用者不用去知道怎么创建对象,使得各个模块各司其职,降低了系统的耦合性。

缺点:就是要增加新的核类型时,就需要修改工厂类。这就违反了开放封闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。

2.工厂方法模式

所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。