【C++进阶】特殊类的设计——单例模式详解

ops/2025/1/7 22:01:01/

单例模式

  • 一,设计一个类不能被拷贝
  • 二,设计一个类,只能在堆上创建对象
  • 三,设计一个类,只能在栈上创建对象
  • 四,设计一个类,不能被继承
  • 五,设计一个类,只能创建一个对象(单例模式
    • 5.1 饿汉模式
    • 5.2 懒汉模式
  • 六,总结

上一节我们讲解了智能指针,到这里C++的有难度的部分已经过去了,现在我们来讲一些其他的知识。
在开发的实际场景中,有很多需求比如一个类不能被拷贝或者规定了其创建对象的地方等。这一节我们就来讲解一些常见的特殊类的设计,当然也有单例模式。废话不多说我们直接开始。

一,设计一个类不能被拷贝

在设计之前我们可以思考一下,拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载
因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
在C++98中,我们可以将拷贝构造函数与赋值运算符重载只声明不定义并且将访问权限设置为私有。

class CopyBan
{// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};

设置为私有是因为如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
能禁止拷贝了
不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

在C++11中,又扩展了delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上 =delete,表示让编译器删除掉该默认成员函数

class CopyBan
{// ...CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;//...
};

二,设计一个类,只能在堆上创建对象

这里有两种方案
方案一是:析构函数私有化

class HeapOnly {public://...private:~HeapOnly() {}};int main() {HeapOnly heap;//static HeapOnly hheap;//静态也不能//所以只能new一个HeapOnly* ptr = new HeapOnly;return 0;
}

像上面代码中的HeapOnly heap是不能定义的,因为析构不了。包括静态的也一样。所以只能在new一个,而new的对象就创建在堆上。

那么如何析构new出来的对象呢?
这里可以单独写一个公有的析构:

class HeapOnly {public://...void Destory() {delete this;}private:~HeapOnly() {}};int main() {HeapOnly* ptr = new HeapOnly;ptr->Destory();//调用公有的去析构return 0;
}

方案二是:构造函数私有化

class HeapOnly {
public://解决办法是写一个公有的构造static HeapOnly* create() {//加上static,来解决先有鸡还是先有蛋的问题return new HeapOnly;}private:HeapOnly() {cout << "HeapOnly()" << endl;}};int main() {//HeapOnly hp1;//static HeapOnly hp2;//HeapOnly* hp3 = new HeapOnly;HeapOnly* ptr = HeapOnly::create();return 0;
}

构造函数私有后,之前的三种常见对象的方式都不能创建对象,所以还是一样,写一个公有的构造函数

注意:这里的create函数加上static才可以调用,否则会出现先有鸡还是先有蛋的问题

但是还有一个小问题,那就是如果拷贝构造的话,那么拷贝构造的这个对象还是在栈上创建的,不是堆。

解决办法就是直接在拷贝函数后面加上 delete🈲掉即可:

HeapOnly(const HeapOnly& hp) = delete;

三,设计一个类,只能在栈上创建对象

这里设计时就不能再将析构函数私有了,但是构造函数私有话还是可以的,构造函数私有是为了不让对象可以被随便创建,私有后再写一个公有的构造,使其只能在栈上创建。

class StackOnly {
public:static StackOnly create() {StackOnly obj;return obj;//返回一个临时拷贝}private:StackOnly() {cout << "StackOnly()" << endl;}
};int main() {//StackOnly st;StackOnly st = StackOnly::create();//StackOnly* ptr = new StackOnly;//不能创建对象return 0;
}

但是这里还是,如果拷贝的话还是创建在了堆上。

StackOnly* ptr = new StackOnly(st);

这里不能简单的将拷贝构造禁掉,那样的话公有的构造将不能返回

这里的解决办法是禁掉自己的operator new,因为new的时候,new是全局的,如果一个类重载了new,那么new时就会调用这个类自己重载的。禁掉自己的new后,再new时会调用这个重载的版本,但是这个又被delete了,所以new不了

void* operator new(size_t size) = delete;

四,设计一个类,不能被继承

这个我们在继承部分讲过,C++11新增加的关键字final,直接在类声明时后面加上final就可以了

class A final
{// ....
};

或者将构造函数私有
C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承

class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};

五,设计一个类,只能创建一个对象(单例模式

这里就牵扯到设计模式了,设计模式一共有23种,我们只要知道几种就好。这里我们讲一下单例模式。分为两种:

5.1 饿汉模式

饿汉模式就是在main函数启动时创建好示例对象。

优点:简单
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定

5.2 懒汉模式

懒汉模式就是在调用这个对象时才创建,就解决了上面的两种问题
但是牵扯到了线程安全问题

对于这两种模式的代码示例,大家可以去我的gitee仓库进行参考: 特殊类设计

六,总结

特殊类的设计我们只要知道常用的就可以了,单例模式还是比较的常用,所以要好好理解一下。下一节我们会带来C++的收尾内容,到这里我们C++的主要内容也快结束了,但是继续深挖C++的话还是有很多东西要学,所以希望大家可以持续学习,我也会带来更多的内容去分享。


http://www.ppmy.cn/ops/9147.html

相关文章

node.js 模版引擎

模版引擎 一、什么是模版引擎&#xff1a; 模版引擎是为了使用户界面与业务逻辑&#xff08;内容&#xff09;分离而产生的&#xff0c;它可以生成特定格式的文档&#xff0c;用于网站的模版引擎就会生成一个标准的文档就是将模版文件和数据通过模版引擎生成一个html文档。 常…

深度学习小白向-如何理解batchsize

什么是batchsize&#xff1f; 一次训练&#xff08;梯度更新&#xff09;所选取的样本数。 batchsize的影响 对准确率的影响 batchsize越大越能得到准确的梯度方向&#xff0c;但是由于深度学习问题一般是非凸问题&#xff0c;所以也容易导致陷入局部最优点。–另外batchsi…

ArrayList与顺序表(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

redis常用5大数据类型

1、string&#xff08;字符串&#xff09; String是Redis中最常用的一种数据类型&#xff0c;也是Redis中最简单的一种数据类型。首先&#xff0c;表面上它是字符串&#xff0c;但其实他可以灵活的表示字符串、整数、浮点数3种值。Redis会自动的识别这3种值。 2、list(列表) …

牛客网华为机试题说明一

一. 简介 对牛客网华为机试题进行一下记录。 二. 牛客网华为机试题 1. 字符串最后一个单词的长度 题目说明 计算字符串最后一个单词的长度&#xff0c;单词以空格隔开&#xff0c;字符串长度小于5000。 &#xff08;注&#xff1a;字符串末尾不以空格为结尾&#xff09; 输…

Mac 安装pnpm报错

npm install -g pnpm 报错截图&#xff1a; 报错原因&#xff1a;权限 解决方法&#xff1a;sudo npm install -g pnpm --allow-root 输入密码即可

数仓建模—数据架构

数仓—数据架构 为了在企业决策中使用数据,数据必须经过整个数据平台的各个阶段。整个过程是什么样子的,从开始到结束?原始形式的数据是如何转化为可导致商业决策的见解的?这些问题可以通过数据架构来回答。 数据架构是指记录组织所有数据资产的模型、规则和标准。它映射…

设计模式之桥接模式

1、详细介绍 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它将抽象部分与其实现部分分离&#xff0c;使它们可以独立变化。桥接模式通过引入抽象层和实现层的概念&#xff0c;允许用户在不改变抽象接口的前提下&#xff0c;动态地选择或替…