文章目录
问题简述
近来 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 模式应用
一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号,否则会出现主键重复,因此该主键编号生成器必须具备唯一性,可以通过单例模式来实现。
7323

被折叠的 条评论
为什么被折叠?



