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

server/2024/12/22 9:05:48/

单例模式

  • 一,设计一个类不能被拷贝
  • 二,设计一个类,只能在堆上创建对象
  • 三,设计一个类,只能在栈上创建对象
  • 四,设计一个类,不能被继承
  • 五,设计一个类,只能创建一个对象(单例模式
    • 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/server/8653.html

相关文章

mysql 删除数据的四种方法

在MySQL数据库中,删除数据是一个常见的操作,它允许从表中移除不再需要的数据,本文就来介绍一下四种方法,具有一定的参考价值,感兴趣的可以了解一下 − 目录 写在前面 方法介绍1. DELETE语句 示例&#xff1a; 2. DROP TABLE语句&#xff1a; 3. TRUNCATE TABLE 示例&#x…

关联规则挖掘(二)

目录 三、FP-增长算法&#xff08;一&#xff09;算法的背景&#xff08;二&#xff09;构造FP-树&#xff08;三&#xff09;生成频繁项集 四、关联规则的评价&#xff08;一&#xff09;支持度和置信度的不足&#xff08;二&#xff09;相关性分析 三、FP-增长算法 &#xf…

Redis中的订阅发布(二)

订阅与发布 订阅频道 每当客户端执行SUBSCRIBE命令订阅某个或某些频道的时候&#xff0c;服务器都会将客户端与被订阅的频道 在pubsub_channels字典中进行关联。 根据频道是否已经有其他订阅者&#xff0c;关联操作分为两种情况执行: 1.如果频道已经有其他订阅者&#xff0c…

WP-AutoPostPro 汉化版: WordPress自动采集发布插件

WP-AutoPostPro 是目前最好用的WordPress自动采集发布插件&#xff0c;最大的特点是可以采集来自于任何网站的内容并自动发布到你的WordPress站点。真正做到可以采集任何网站的内容并自动发布&#xff0c;采集过程完全自动进行无需人工干预&#xff0c;并提供内容过滤、HTML标签…

实现联系人前后端界面,实现分页查询04.15

实现联系人前后端界面&#xff0c;实现分页查询项目包-CSDN博客 项目结构 数据库中建立两个表&#xff1a; 完整的后端目录 建立联系人People表&#xff0c;分组Type表&#xff0c;实现对应实体类 根据需求在mapper中写对应的sql语句 查询所有&#xff0c;删除&#xff0c;添…

TypeScript24:TS中的声明文件

一、声明文件概述 以 .d.ts 结尾的文件。 声明文件作用&#xff1a; ts->js &#xff0c;得不到类型声明。 为 JS 代码提供类型声明。 声明文件书写的位置&#xff1a; 放置到 tsconfig.json 配置中包含的目录中&#xff1b;放置到 node_modules/types 文件夹中&#x…

Linux虚拟化性能损失:原因、评估与优化策略

在云计算和数据中心领域&#xff0c;Linux虚拟化作为基础设施的核心组件&#xff0c;为资源的高效利用和应用程序的灵活部署提供了坚实的基础。然而&#xff0c;尽管其优势显著&#xff0c;虚拟化环境下的性能损失问题仍然是一个不可忽视的挑战。本文将深入探讨Linux虚拟化中性…

Vue页面生成导出PDF文件

第一种&#xff1a; 使用浏览器自带打印方法window.print(); 也可使用print-js插件&#xff08;原理相同&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>printDemo</title> </…