特殊类的设计笔试题———单例模式的类(懒汉模式,饿汉模式)
Posted 努力学习的少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了特殊类的设计笔试题———单例模式的类(懒汉模式,饿汉模式)相关的知识,希望对你有一定的参考价值。
- 💂 个人主页:努力学习的少年
- 🤟 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
目录
1. 请设计一个类,只能在堆上创建对象
实现:
将类的构造函数和拷贝构造函数设置为私有,提供一个静态函数GetHeapOnly(),在该函数完成堆对象的创建。
为什么要类的构造函数和拷贝构造函数定义成私有函数?
- 将类的构造函数和拷贝构造函数定义成私有函数是为了让类外部不能调用这两个函数,因为构造函数被禁用,所以类外面就不能随意定义出对象,只能通过GetHeapOnly()函数创建堆对象,拷贝构造函数被禁用,防止别人调用拷贝构造函数创建出栈对象。
为什么要将GetHeapOnly()函数定义成静态成员函数?
- 如果GetHeapOnly()是非静态成员函数,那么该函数只能被该类型的对象调用,由于类外部不能创建出对象,那么该函数就永远不能够被调用,这涉及到先有鸡还是先有蛋的问题,因此,我们将该函数定义成静态成员函数,那么类外面就可以通过HeapOnly::GetHeapOnly()调用该函数。
2.请设计一个类,只能在栈上创建对象
实现:
- 将类的构造函数和拷贝构造函数定义成私有函数,然后提供一个静态函数GetHeapOnly(),这个函数是在栈上创建对象。原理与第1题是一样的。
3.设计一个类不能被拷贝。
拷贝只出现在两个场景中:
- 拷贝构造函数以及赋值运算符重载函数,因此我们只需要禁用这两个函数,就可以将使类的对象不能被拷贝。
c++98的做法
- 思路:将拷贝构造函数和赋值运算符重载函数只声明不定义,并且都设置为private。
c++11做法
- 在函数后面加上=delete表示成员函数被禁用,所以我们可以在拷贝构造函数和赋值重载函数中加上=delete。
4. 设计一个类不能被继承
c++98做法
- 将基类的构造函数设置为private,这样派生类就调不到基类的构造函数,所以该基类就不能被继承。
c++98这种方法只有派生类实例出对象才会报错。
c++11
- c++11的做法是直接在基类定义的后面加上final,表示该类不能被继承,如果派生类想继承该基类,则编译器会直接报错。
5. 单例模式的类
什么是单例模式?
一个类只能创建一个对象。该模式可以保证系统中该类只有一个实例对象。
单例模式的思路
- 把构造函数私有化,限制外部创建该类对象,然后将拷贝构造函数和函数重载赋值函数給删除掉。
- 在类中定义一个静态的类对象指针,定义一个可以被类外部调用的函数GetLayBones(),该函数能够返回的是该类对象指针,保证每次调用GetLayBones()函数都返回同一个对象指针。
为什么类对象的指针要设置为静态?
- 静态的类对象指针是全局区域的,它的生命周期是随进程的,所以当一个进程内当有多个线程时,调用该函数时,返回的是同一个对象指针
- GetLayBones()函数是静态成员函数,没有this指针,如果index是非静态成员变量,那么GetLayBones()不能访问该变量。
类对象指针指向的是一个类对象,这个类对象可以是在类程序开始运行时(main函数之前)就被创建出来,这种模式称之为单例的饿汉模式,如果这个类对象是在第一次调用GetLayBones()时才被创建出来,这种模式称之为单例的懒汉模式。
饿汉模式
- 在类加载的时候就已经创建好对象,也就是在main函数开始之前就已经创建好了对象。如果类对象很大时,程序启动会因创建对象的时候出现延迟的现象。
懒汉模式
- 如果这个类对象是在第一次调用GetLayBones()时才被创建出来,这种模式称之为单例的懒汉模式。懒汉模式的类对象指针首先在类外将该指针初始化为nullptr,如果外部调用GetLayBones()函数时,那么GetLayBones()函数里面需要做的判断是否是第一次调用该函数,如果是第一次调用该函数,那么就在创建出对象,然后对象指针赋值给类对象的指针。
- 如果有多个线程同时第一次调用该函数时,那么该函数就会出现线程不安全的问题,举个例子:如果线程1和线程2同时通过if判断进入if内部,假设线程1运行快一些,创建好了对象A,然后将对象A的地址赋值给index指针,线程1通过index指针使用对象A做了很多事情,此时线程1结束后,线程2创建了一个对象B,然后线程2将对象B的地址赋值给index指针,那么之前index指针指向对象A变成了指向对象B,这造成线程1之前所作的工作都白费掉了,也不符合单例模式只能创建出一个对象的条件,更造成进程的内存泄漏。所以我们就需要加锁限制一次只能有一个线程进入if条件内部。加锁后的代码如下:
- 如果对象已经被创建出来后,那么后面如果有多个线程调用GetLayBones()函数获得该对象的指针时,因为if (index == nullptr)指令被lock锁起来了,而线程要获得对象指针就必须经过该指令,一次只能有一个线程能进入lock内部的临界区,因此一次就只能有一个线程获得对象指针。这多线程的效率就有所下降,为了让多线程调用能够同时获得GetLayBones()函数时,能够在同时获得对象指针。所以我们就需要在lock()多定义一个if (index == nullptr),使创建好类对象能够绕开lock()。
单if判断:
在lock外部加上一个if判断,提高多线程的效率。
对象创建完后,index不为nullptr,当多个线程调用GetHungryMan()函数。
饿汉模式和懒汉模式的对比
饿汉模式
- 优点:简单,程序加载后就已经将对象给创建好了,不需要考虑线程安全问题。
- 缺点:如果单例对象构造函数初始化比较多,会导致程序启动慢,迟迟不会进入main函数里面。
懒汉模式
- 优点:不会出现程序启动慢的问题,只有当第一次调用GetLayBones()函数才会创建单例对象。
- 缺点:需要考虑线程安全问题,较为复杂。
以上是关于特殊类的设计笔试题———单例模式的类(懒汉模式,饿汉模式)的主要内容,如果未能解决你的问题,请参考以下文章