C++ 程序设计:单例+原型(手机原型机和量产机)

1.简介

1.1单例模式

C++单例模式被广泛应用于需要全局唯一实例的场景。以下是一些常见的使用场景:

  1. 日志记录器
    在大多数应用程序中,需要一个全局的日志记录器来记录系统运行时的事件和错误。使用单例模式可以确保只有一个日志记录器实例,并能够在整个应用程序中共享和访问。

  2. 数据库连接池
    数据库连接是有限且昂贵的资源,为了避免频繁地创建和销毁连接,可以使用单例模式来管理数据库连接池。这样可以确保在应用程序中只有一个连接池实例,并且可以在需要时轻松获取数据库连接。

  3. 配置管理器
    配置管理器负责加载和管理应用程序的配置信息,例如数据库连接字符串、日志级别等。通过单例模式,可以确保只有一个配置管理器实例,并能够在整个应用程序中统一访问配置信息。

  4. 全局对象管理器
    在某些应用程序中,可能存在需要全局访问的对象,例如全局事件管理器、全局资源管理器等。使用单例模式可以确保这些全局对象只有一个实例,并能够在整个应用程序中方便地进行访问和使用。

  5. 线程池
    线程池用于管理和调度多个线程执行任务,通过单例模式,可以确保只有一个线程池实例,并能够在整个应用程序中灵活地进行任务分发和执行。

这些只是一些常见的使用场景,实际上,单例模式可以适用于任何需要一个全局唯一实例的情况。但是,需要注意的是,单例模式可能会带来全局共享状态的问题,因此在使用单例模式时需要仔细考虑其潜在的风险和副作用。同时,还要记住,在多线程环境中使用单例模式需要考虑线程安全性。

1.2 原型模式

      原型模式是一种通过复制现有对象来创建新对象的设计模式。它使用一个原型对象作为创建对象的样板,通过克隆原型对象来生成新的对象,而不是使用传统的实例化过程。
      原型模式的核心思想是使用已有对象作为原型,通过复制(克隆)这个原型来创建新的对象,而不是通过传统的实例化方式。这种方式允许我们在运行时动态地创建对象,并且可以避免耗费时间和资源去重新初始化对象。

1.3 比喻-手机原型机和量产机

原型模式和单例模式可以这样比喻我们要制作一款手机:

  1. 原型模式比喻为手机的制作过程中,我们先制作一个原型机,并通过复制原型机来制造更多的手机。原型机是一个已经设计好的模板,我们可以根据它来制造新的手机,而每个手机都可以独立使用和修改。这样,我们可以根据原型机的特性和功能来扩展和修改手机,从而得到多个不同的手机实例。

  2. 单例模式比喻为手机的制造过程中,我们只允许制造一台手机,这台手机成为该品牌的唯一代表。无论是在什么地方,我们都只能获得并使用这一台手机。这样做的好处是,我们可以保证这台手机的唯一性,避免资源浪费,并且方便全局访问。

2.单例的简单实现

class SysParaData : 
{
public:
    static SysParaData &instance()
    {
        static SysParaData self;
        return self;
    }

private:

    SysParaData();
    ~SysParaData();
    SysParaData(const SysParaData &self);
    const SysParaData &operator=(const SysParaData &self);
};

这里用单例模式定义了一个全局只有一个的数据参数类,用来存储全局的参数。

    在类中,instance()是一个公共的静态成员函数,用于返回SysParaData类的唯一实例。这个函数通过局部静态变量self来实现,在第一次调用instance()函数时创建实例。由于局部静态变量在函数退出后仍然存在,所以每次调用instance()函数都会返回同一个实例。

   同时,构造函数、析构函数、拷贝构造函数和赋值运算符被声明为私有,这意味着不能从类的外部直接实例化、销毁、复制或赋值SysParaData对象。这样可以确保只有instance()函数可以创建和访问SysParaData类的唯一实例。

    单例模式的优点之一是可以提供全局唯一的对象访问点,方便在程序中的任何地方使用该对象。通过调用SysParaData::instance()函数,可以获得对SysParaData类的唯一实例的引用。

注意:这里没有进行多线程安全的处理。如果在多线程环境中使用该单例模式,可能需要额外的线程同步措施,以确保线程安全。例如使用互斥锁或其他线程同步机制来保护实例的创建和访问过程。

3.原型模式的简单实现

// 原型类
class SysParaModifyDataBase
{
public:
    virtual SysParaModifyDataBase *Clone() = 0;
};

//
class SysParaModifyData: public SysParaModifyDataBase
{
public:
    SysParaModifyData()

    virtual Prototype *Clone()
    {
        return new SysParaModifyData(*this);
    }
    int data = 1;
};

SysParaModifyDataBase *dataSource = new SysParaModifyData();
SysParaModifyDataBase *dataModify = dataSource ->Clone();

4.原型模式和单例模式的结合

class SysParaData :
{
private:
    static SysParaData *instance; 

public:
    /* 禁止拷贝构造函数 禁止拷贝赋值运算符的另一种写法 与上面单例代码设为私有效果一样 */
    SysParaData (const SysParaData &) = delete;
    SysParaData & operator=(const SysParaData &) = delete;

    /* 单例实例的另一种写法 */
    static SysParaData *getInstance() 
    {
        if (instance == nullptr) 
        {
            instance = new SysParaData ();
        }
        return instance;
    }

    void setName(const std::string& newName) 
    {
        name = newName;
    }

    std::string getName() const 
    {
        return name;
    }

    SysParaData *clone() const 
    {
        SysParaData *cloneInstance = new SysParaData ();
        cloneInstance->setName(this->name);
        return cloneInstance;
    }
};

如果使用clone()方法克隆一个SysParaData对象,并使用该克隆对象调用setName()修改名称,那么原始的单例对象将不受影响,它们是相互独立的。因此,克隆对象和原始单例对象不再是同一个单例。

单例模式的规则是一个类只能有一个实例,并且通过getInstance()方法获取单例实例。在这个例子中,我们使用静态变量instance来存储唯一的单例实例,而且通过禁止拷贝构造函数和拷贝赋值运算符的使用来确保只有一个实例。克隆方法clone()是为了使用已有的单例对象来创建新的对象实例。

     被克隆之后的对象如果不使用clone()方法创建,而是直接使用单例对象的指针进行赋值或传递,那么它们仍然是指向同一个单例的指针。

例如:

SysParaData *singleton = SysParaData::getInstance();
SysParaData *cloneInstance = singleton->clone();  // 克隆单例对象

SysParaData *directAssignment = singleton;        // 直接赋值
SysParaData *functionParameter = cloneInstance;   // 作为函数参数传递

// 修改单例对象的名称
singleton->setName("Singleton");

// 输出对象的名称
std::cout << "Singleton Name: " << singleton->getName() << std::endl;
std::cout << "Direct Assignment Name: " << directAssignment->getName() << std::endl;
std::cout << "Function Parameter Name: " << functionParameter->getName() << std::endl;

输出结果将会是:

Singleton Name: Singleton
Direct Assignment Name: Singleton
Function Parameter Name: Cloned Instance

可以看到,直接赋值或作为函数参数传递的对象指针仍然指向同一个单例对象,因此它们的名称相同。只有通过clone()方法创建的对象是一个独立的克隆实例,它们和原始单例对象是不同的。

因此,被克隆之后的对象,如果不使用clone()方法创建,而是直接使用单例对象的指针进行赋值或传递,仍然是指向同一个单例实例,因此仍然是单例。

5.总结

    当原型模式与单例模式结合在一起时,可以实现一个可以克隆的单例。

     在传统的单例模式中,类只能有唯一的一个实例,并且使用静态方法获取该实例。而在原型模式中,可以通过复制原型对象来创建新的对象实例。

    当将这两种模式结合在一起时,通过克隆原型对象,可以创建多个具有相同属性和行为的实例,同时保持单例的特性,即每次克隆都得到同一个实例。

    这种结合模式的应用场景是当希望在单例基础上创建新的对象实例,这些新实例保持与原始实例相同的初始状态,并且这些实例之间相互独立。

    使用这种结合模式,可以方便地创建多个独立且具有相同初始状态的对象,同时避免了在使用时传递参数的麻烦。它提供了更大的灵活性和可扩展性,同时仍然保持了单例的特性。

注意:此结合模式的实现需要对原型对象进行深度拷贝,以确保每个克隆对象是独立的。否则,如果使用浅拷贝,所有克隆对象将共享同一个状态,这不符合原型模式的预期。

  • 22
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值