面试题2:实现Singleton 模式

题目:设计一个类,我们只能生成该类的一个实例。

  只能生成一个实例的类是实现了Singleton (单例)模式的类型。由于设计模式在面向对象程序设计中起着举足轻重的作用,在面试过程中很多公司都喜欢问一些与设计模式相关的问题。在常用的模式中, Singleton是唯一一个能够用短短几十行代码完整实现的模式。因此,写一个Singleton的类型是一个很常见的面试题。

单例模式有三种经典的设计方案:

  1. 延时加载,也称为懒汉模式 ,需要的时候才创建对象,也就是下面方法一用到的
  2. 双重锁模式,需要的时候才创建对象,线程安全,懒汉模式升级版,也就是下面方法二用到的
  3. 贪婪加载,也称为饿汉模式 , 程序执行前就创建好对象,也就是下面方法三用到的
  4. 测试,简单测试。

  解法一:只适用于单线程环境

  由于要求只能生成一个实例,因此我们必须把构造函数设为私有函数以禁止他人创建实例。我们可以定义一个静态的实例,在需要的时候创建该实例。下面定义类型Singletonl就是基于这个思路的实现;

class SingleTon
{
public:
static SingleTon* getInstance()
{
if (instance== NULL)
{
instance= new SingleTon(); //堆区申请对象
} return instance;
}
private:
SingleTon(){} //隐藏构造函数接口
SingleTon(const SingleTon&);//拷贝构造函数
static SingleTon* instance; //静态指针可以不依赖对象调用,保存唯一实例地址
};
SingleTon* SingleTon::instance= NULL;

  上述代码在Singleton的静态属性Instance中,只有在instance为null的时候才创建一个实例以避免重复创建。同时我们把构造函数定义为私有函数,这样就能确保只创建一个实例。

  注意:只适用于单线程环境,在多线程中,如果一个现成在刚完成if (instance== NULL)这段代码时,还没有来的及创建对象时,另一个线程也执行到这个阶段,如此一来,程序就会出现问题,也不满足我们单例模式的目的。

  加同步锁:if (instance== NULL)这段语句前后加锁,实现每次只能有一个线程访问

  通常在解决线程安全类问题常用的方法就是加锁,限制对临界区的访问。下面这段代码虽然解决线程安全问题,但程序的效率却低了不少。

class SingleTon
{
public:
static SingleTon* getInstance()
{
lock(); //加锁 注:并未具体实现此函数,这里仅仅做演示
if (instance== NULL)
{
instance= new SingleTon(); //堆区申请对象
}
unlock(); //解锁 return instance;
}
private:
SingleTon(){} //隐藏构造函数接口
SingleTon(const SingleTon&);//拷贝构造函数
static SingleTon* instance; //静态指针可以不依赖对象调用,保存唯一实例地址
};
SingleTon* SingleTon::instance= NULL;

  加锁解锁机制: 线程A在执行到lock()时,如果发现无法加锁,即有别的线程B正在使用(加锁)此临界资源,当A发现可以进行加锁操作时,即B已经执行完成并进行了解锁操作,此时A再加锁(防止别的线程执行),当A线程执行完遇到代码unlock()时进行解锁操作。

  但是,还有一个问题,当唯一的一个实例已经被创建出来后,后面的线程执行到此段代码时,无需再构建实例,但lock() unlock()会被反复执行,程序进行了许多不必要的开销,降低了效率。

难道真的鱼(线程安全)与熊掌(效率)不可兼得乎?

方法二:双重锁模式

  双重锁模式:在加锁前再判断一次,是否需要执行以下内容。虽然叫双重锁,可并不是真正的加了两重锁哦,只是利用了一点技巧而已,可见,有时候只要多想一点点总会有办法的嘛。

class SingleTon
{
public:
static SingleTon* getInstance()
{
if (instance== NULL) //对象已经生成,无需加锁、解锁操作
{
lock();
if (instance== NULL)
{
instance= new SingleTon();
}
unlock();
}
return instance;
}
private:
SingleTon(){}
SingleTon(const SingleTon&);
static SingleTon* instance;
};

方法三:提前创建实例

由于静态变量在代码段存放,在程序刚被加载时就已经存在,因此可以利用这一特点重新设计程序,直接构造实例并让静态的指针instance接收。

class SingleTon
{
public:
static SingleTon* getInstance()
{
return instance;
}
private:
SingleTon(){}
SingleTon(const SingleTon&);
static SingleTon* instance;
};
SingleTon* SingleTon::instance= new SingleTon();

测试:

Person类,属性有mname mage msex

class Person
{
public:
static Person* getInstance(char* name, int age, bool sex)
{
if (pm == NULL)
{
pm = new Person(name, age, sex);
}
return pm;
}
~Person()
{
delete[] mname;
mname = NULL;
}
private:
Person(char* name, int age, bool sex)
{
mname = new char[strlen(name) + 1]();
strcpy_s(mname, strlen(name) + 1, name);
mage = age;
msex = sex;
}
char* mname;
int mage;
bool msex;
static Person* pm;
};
Person* Person::pm = NULL; int main()
{
char name[] = "zhangsan";
Person* p1 = Person::getInstance(name, 25, true);
Person* p2 = Person::getInstance(name, 25, true);
Person* p3 = Person::getInstance(name, 25, true);
Person* p4 = Person::getInstance(name, 25, true);
return 0;
}

运行结果:



可以看到运行结果中,不论getInstance方法调用多少次,始终只有一个实例产生。

《剑指offer》面试题2:实现Singleton 模式的更多相关文章

  1. 剑指Offer:面试题15——链表中倒数第k个结点(java实现)

    问题描述 输入一个链表,输出该链表中倒数第k个结点.(尾结点是倒数第一个) 结点定义如下: public class ListNode { int val; ListNode next = null; ...

  2. 剑指offer面试题3 二维数组中的查找(c)

    剑指offer面试题三:

  3. 剑指Offer——笔试题+知识点总结

    剑指Offer--笔试题+知识点总结 情景回顾 时间:2016.9.23 12:00-14:00 19:00-21:00 地点:山东省网络环境智能计算技术重点实验室 事件:笔试 注意事项:要有大局观, ...

  4. C++版 - 剑指offer之面试题37:两个链表的第一个公共结点[LeetCode 160] 解题报告

    剑指offer之面试题37 两个链表的第一个公共结点 提交网址: http://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?t ...

  5. C++版 - 剑指offer 面试题23:从上往下打印二叉树(二叉树的层次遍历BFS) 题解

    剑指offer  面试题23:从上往下打印二叉树 参与人数:4853  时间限制:1秒  空间限制:32768K 提交网址: http://www.nowcoder.com/practice/7fe2 ...

  6. C++版 - 剑指offer 面试题39:判断平衡二叉树(LeetCode 110. Balanced Binary Tree) 题解

    剑指offer 面试题39:判断平衡二叉树 提交网址:  http://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId= ...

  7. Leetcode - 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)

    剑指offer 面试题29:数组中出现次数超过一半的数字 提交网址: http://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163 ...

  8. C++版 - 剑指Offer 面试题39:二叉树的深度(高度)(二叉树深度优先遍历dfs的应用) 题解

    剑指Offer 面试题39:二叉树的深度(高度) 题目:输入一棵二叉树的根结点,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度.例如:输入二叉树 ...

  9. C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解

    剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...

  10. C++版 - 剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题,ZOJ 1088:System Overload类似)题解

    剑指Offer 面试题45:圆圈中最后剩下的数字(约瑟夫环问题) 原书题目:0, 1, - , n-1 这n个数字排成一个圈圈,从数字0开始每次从圆圏里删除第m个数字.求出这个圈圈里剩下的最后一个数字 ...

随机推荐

  1. mybatis plus框架的@TableField注解不生效问题总结

    一.问题描述 最近遇到一个mybatis plus的问题,@TableField注解不生效,导致查出来的字段反序列化后为空 数据库表结构: CREATE TABLE `client_role` ( ` ...

  2. idea常用快捷键及配置

    目录 常用快捷键 常用配置 配置修改项 版本2019.1.3,配置.破解插件见网盘 2020.3.4 链接:https://pan.baidu.com/s/1WHsS8-yvHGf1iRopLbKIu ...

  3. Weblogic补丁升级常见问题

    转至:https://blog.csdn.net/weixin_44659716/article/details/106804177 常用企业级版本:Weblogic 11g(weblogic10.3 ...

  4. Python:Scrapy(三) 进阶:额外的一些类ItemLoader与CrawlSpider,使用原理及总结

    学习自:Python Scrapy 爬虫框架实例(一) - Blue·Sky - 博客园 这一节是对前两节内容的补充,涉及内容为一些额外的类与方法,来对原代码进行改进 原代码:这里并没有用前两节的代码 ...

  5. java-排查

    https://www.chinacion.cn/article/4271.html https://blog.csdn.net/zhengwei223/article/details/7715122 ...

  6. lsof、tcpdump和wireshark

    lsof:https://www.jianshu.com/p/a3aa6b01b2e1 tcpdump:https://linux.cn/article-10191-1.html wireshark: ...

  7. selenium+python自动化-元素定位

    自动化只要掌握四步操作:获取元素,操作元素,获取返回结果,断言(返回结果与期望结果是否一致),最后自动出测试报告. 本篇主要讲几种基本的元素定位方法元素定位在这四个环节中是至关重要的,如果说按学习精力 ...

  8. PHP pdf转png windows版本

    链接:https://pan.baidu.com/s/1Bli-2HkucRTYTeujkcsmjg&shfl=sharepset 提取码:2une 1.php_imagick扩展 (1)下载 ...

  9. mysql 完整备份和恢复

    mysql 完整备份和恢复   一.MySQL完整备份操作 1.直接打包数据库文件夹 创建数据库auth: MariaDB [(none)]> create database auth;Quer ...

  10. 对于处理datetime数据类型的一些常用方法:

    datetime数据类型常用方法: 在项目中从数据库中取出数据后通常需要先绘制图像进行数据的观察,此过程中使用到的方法: 1.时间数据类似于 2022-03-23 14:21:45 可以先转换为dat ...