单例模式

问题简述

主要参考链接

近来 C++ 用到单例模式,(单例模式是啥不说了,网上大把的资料),结果编译的时候遇到 undefined reference to `XXX’ XXX 是单例模式中的那个静态私有指针。结果是由于变量没有初始化引起。虽然解决之后听起来很小白,但着实用了很多时间,所以决定做个记录。

来龙去脉

写了个单例模式,大概这样

//Single.h

class Single(){

public:
    static Single * GetInstance();

private:

    static Single * instance;
    Single(){};
}
//Single.cc


Single* Single::GetInstance(){
    if(instance == NULL){
        instance = new Single();
    }
    return instance;
}

其他部分略去不表,结果编译时报错

 undefined reference to `instance' 

百思不得其解啊,怎么能找不到呢,很清楚的声明了啊

声明 != 定义

声明:是的名字为程序所知

定义:负责创建与名字关联的实体(摘自 C++ primer)

按照字面意思理解似乎声明完了就不应该报 undefine reference 错才对,但考虑到程序在定义是才给变量分配内存,所以理解为指向类实例的指针只有定义过了才能使用

解决方法

这就很简单了,在 Single.cc 中加上

Single* Single::instance = NULL

也就是定义 instance 变量就行。不要怀疑,private 类型的 static 变量也可以这么定义。 static 变量其实不属于任何类。

static成员函数特点

  • 因为static成员函数没有this指针,所以静态成员函数不可以访问非静态成员。
  • 非静态成员函数可以访问静态成员。
  • 静态数据成员与类的大小无关,因为静态成员只是作用在类的范围而已。

参考链接

参考链接

5. 单例模式

5.1 模式动机

系统中的某些类,只能有一个实例,如时间统计函数,打印机等。一般会被实例化为全局静态函数。

5.2 模式定义

单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

5.3 模式结构

单例模式值包含单例(Singleton)一个角色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-40WcYnWc-1612337788510)(https://design-patterns.readthedocs.io/zh_CN/latest/_images/Singleton.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XExVnaL0-1612337788514)(https://design-patterns.readthedocs.io/zh_CN/latest/_images/seq_Singleton.jpg)]

5.5 代码分析

#include <iostream>
#include <string>

class Timer {
 public:
  ~Timer() {
    if (instance_) delete instance_;
    instance_ = NULL;
  }
  static Timer* GetInstance() {
    Timer* instance_ = nullptr;
    if (instance_ == nullptr) {
      instance_ = new Timer();
    }
    return instance_;
  }

  void Use() { std::cout << "Use Timer" << std::endl; }

 private:
  Timer() {}

  static Timer* instance_;
};

class Singlton {
 public:
  ~Singlton() {}
  static Singlton GetInstance() {
    static Singlton install_;
    install_.words = std::string("Hello");
    return install_;
  }
  void Use() { std::cout << "Use Singleton: " << words << std::endl; }

 private:
  Singlton() {}
  std::string words;
};

int main() {
  auto timer = Timer::GetInstance();
  timer->Use();

  auto singlton = Singlton::GetInstance();
  singlton.Use();
}

5.6 模式分析

单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

在单例模式的实现过程中,需要注意如下三点:

  • 单例类的构造函数为私有;
  • 提供一个自身的静态私有成员变量;
  • 提供一个公有的静态工厂方法。

5.8 优点

  • 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
  • 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
  • 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

5.9 缺点

  • 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

5.10 适用环境

  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
  • 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式。

5.11 模式应用

一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号,否则会出现主键重复,因此该主键编号生成器必须具备唯一性,可以通过单例模式来实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值