C++——特殊类设计

C++——特殊类设计

布隆过滤器 (2)

文章目录

  • C++——特殊类设计
    • 特殊类
      • 设计一个类不能被拷贝
      • 设计一个类只能在堆上创建
      • 设计一个类只能在栈上创建
      • 设计一个类不能被继承
    • 单例模式
      • 饿汉模式
      • 懒汉模式

特殊类

设计一个类不能被拷贝

拷贝只会放在两个场景,其一是拷贝构造函数,其二是赋值运算符重载,因此想让一个类不能被拷贝,只需要让该类不能调用拷贝构造函数以及赋值运算符重载,即禁用拷贝构造函数以及赋值运算符重载

  • C++98的做法
class CopyBan
{// ...
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
  • 把拷贝构造和拷贝赋值设置成私有成员,防止在类外调用。
  • 只声明不定义,防止成员函数内部进行拷贝。
  • C++11的做法
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};
  • C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上 =delete,表示让编译器删除掉该默认成员函数。

设计一个类只能在堆上创建

将构造函数私有化,禁用拷贝构造函数

//只能在堆上创建对象
class HeapOnly
{
public:static HeapOnly* Createobj()
//用static修饰的函数不再单单属于对象,而是属于类,可以通过类内函数调用。
//但如果不用static修饰,那么调用类的函数意味这先有对象,而外部调用不了构造函数就不能创建对象{return new HeapOnly();}
private:HeapOnly() {};//禁用构造函数,防止外部调用构造函数在栈上创建对象HeapOnly(const HeapOnly& hp) = delete;//防拷贝
};int main()
{//HeapOnly* hps = new HeapOnly();//这样走需要调用构造函数,然而构造函数不能被调用HeapOnly* hps = HeapOnly::Createobj();}
  • 构造函数私有化,防止外部调用构造函数在栈上、在静态区上创建对象。
  • 拷贝构造用delete修饰,防止通过拷贝构造在栈上、在静态区上创建对象。
  • Createobj函数返回一个new 出来的HeapOnly的匿名对象,用HeapOnly指针接收;
  • Createobj函数用static修饰表示该函数不属于对象,而是属于类,可以通过类内函数调用。否则调用对象的函数前需要先有对象,而调用Createobj函数的作用是获取一个对象,这样就出现问题。

设计一个类只能在栈上创建

方法一

构造函数私有化,通过拷贝构造在栈上创建对象

//只在栈上创建的对象
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();//}
private:StackOnly() {};//构造函数私有。外部通过new对象也需要走构造函数};int main()
{//StackOnly* st = new StackOnly();//构造函数私有化,不能通过new创建对象即不能在堆上创建对象//static StackOnly sp1;//要走构造函数static StackOnly st1 = StackOnly::CreateObj();//没有封掉拷贝构造函数,这里可以通过拷贝构造函数在静态区上创建StackOnly st2= StackOnly::CreateObj();//可以通过拷贝构造在栈上创建对象,但无法防止在静态区上创建return 0;
}
  • 构造函数私有化,防止调用构造函数在堆上、在静态区上创建对象。
  • 调用类内函数获取对象,然后通过拷贝构造在栈上创建对象,但不能防止通过拷贝构造在静态区上创建对象

方法二

将构造函数私有化,禁用拷贝构造函数

class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();//}void Print()const{cout << "StackOnly::Print()" << endl;}~StackOnly() { cout << "~StackOnly()" << endl; }void* operator new(size_t s) = delete;void operator delete(void* p) = delete;//把new和delete禁掉,不能通过new在堆上创建对象
private:StackOnly() {};//构造函数私有。外部通过new对象也需要走构造函数StackOnly(const StackOnly& st) = delete;//防拷贝---防止通过调用拷贝构造在静态区上创建对象};
int main()
{//static StackOnly sp1;//要走构造函数StackOnly::CreateObj().Print();//通过匿名对象在栈上创建对象,走完这一行就调用析构函数释放匿名对象const StackOnly& so4 = StackOnly::CreateObj();//通过const 引用接收匿名对象,匿名对象的生命周期转化为so4对象的生命周期,即cosnt引用延迟了匿名对象的生命周期。匿名对象在栈上创建so4.Print();return 0;
}
  • 构造函数私有化,防止调用构造函数在堆上、在静态区上创建对象。
  • 拷贝构造用delete修饰,防止通过拷贝构造在栈上、在静态区上创建对象。
  • 把new和delete禁掉,不能通过new在堆上创建对象。
  • CreateObj获取一个匿名对象,通过匿名对象调用成员函数Print完成在栈上创建对象。但不好的是该匿名对象的声明周期只有一行,即调用完Print函数对象就立即被销毁。
  • 通过const引用接收匿名对象,匿名对象的生命周期转化为so4对象的生命周期,即cosnt引用延迟了匿名对象的生命周期。匿名对象在栈上创建。

设计一个类不能被继承

C++98方式

class Father
{
public:private:Father() { cout << "get Father" << endl; };
};class Son :public Father
{
public:Son() { cout << "get son" << endl; };const Father& getf(){return Father();}
};
  • 将基类的构造函数私有化,派生类无法调用基类的构造函数,就无法继承。

C++11方法

//基类用final修饰则表示为最后一一个类,不能被继承
class Father final
{
public:Father() { cout << "get Father" << endl; };//构造函数私有化则该基类无法被继承
private:};class Son :public Father
{
public:Son() { cout << "get son" << endl; };const Father& getf(){return Father();}
};
  • 用final修饰基类表示该类为最后一个类,该类无法被继承。

单例模式

设计模式

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式

一个类只能创建一个对象且全局内该类只能有这一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

例如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。单例模式有两种常用的实现模式,其一是饿汉模式,其二是懒汉模式。

饿汉模式

  • 饿汉模式较为简单。
  • 无论后续程序使不使用,在main函数之前就创建好该对象。
  • 缺点在于:一是若饿汉模式的对象诸多,可能导致项目启动速度慢;二是创建的对象顺序由OS决定,若创建的对象之间存在顺序性可能导致出错。例如创建A对象前需要B对象提供接口,那么在饿汉模式下OS先创建的A对象后创建的B对象,会导致A对象创建时B对象不存而调用不到接口而创建A对象失败。

#pragma once
#include<iostream>
#include<map>
#include<string.h>
#include<mutex>
using namespace std;
class SingalInfo
{
public:static SingalInfo&getbody(){return _sig;}void Insert(const string& name,int salary){_info[name] = salary;}void Print(){for (auto& kv : _info){cout << kv.first << " : " << kv.second << endl;}cout << endl;}
private:SingalInfo() {};//构造函数私有化SingalInfo(SingalInfo& sig) = delete;//禁用拷贝构造SingalInfo& operator=(SingalInfo& sig) = delete;//禁用拷贝赋值
private:static	SingalInfo _sig;//类内声明---整个类只有一个对象map<string, int> _info;};
SingalInfo SingalInfo::_sig;//类外定义对象,全局定义具有全局属性,声明周期是全局int main()
{SingalInfo& kk = SingalInfo::getbody();kk.Insert("me", 10000);kk.Insert("you", 20000);kk.Insert("who", 30000);kk.Print();SingalInfo::getbody().Insert("she", 25000);SingalInfo::getbody().Print();kk.Print();//此时kk对象打印的内容和前面的匿名对象打印的内容是一样的表示全局内对象只有一份return 0;
}
  • 构造函数私有化,防止外部调用构造函数创建对象。
  • 拷贝构造、拷贝赋值用delete修饰,禁止通过拷贝创建对象。
  • 类内成员_sig的类型是SingalInfo,表示该成员的类型是当前类。
  • 类内成员_sig用static修饰,表示该成员不单单属于对象,而是属于整个类,表示一个类内只有一个对象。
  • 类内成员_sig在类中声明,在类外全局定义,表示该成员具有全局属性。
  • 通过getbody函数获取_sig成员,用SingalInfo引用接收。外部通过调用getbody函数获取类对象用引用接收。
  • 类内有一份map,用来储存数据。

image-20230909154437081

注意一下:如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化, 就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

  • 懒汉模式下是在main函数启动后创建对象的。
  • 启动顺序可以由我们决定。
  • 缺点在于实现比较复杂。
#pragma once
#include<iostream>
#include<map>
#include<string.h>
#include<mutex>
using namespace std;template<class lock>
class LockGuard
{
public:LockGuard(lock& lk) :_lk(lk){_lk.lock();}~LockGuard(){_lk.unlock();}lock& _lk;
};class SingalInfo
{
public:static SingalInfo& getbody(){//当多个线程进来时,会new多个对象,因此需要加锁保护if (_sig == nullptr)//第一次进来时指针为空,就创建新对象,否则直接返回此对象{LockGuard<mutex> lg(_mut);//	_mut.lock();//双重检查:第一次进来且只有一个线程能够new对象if (_sig == nullptr){_sig = new SingalInfo();}//	_mut.unlock();//这样出现new对象失败抛异常导致解锁失败}return *_sig;}
//static void DeleteInstance(){//...保存数据LockGuard<mutex> lg(_mut);if (_sig){cout << "~ _sig" << endl;delete _sig;_sig = nullptr;}}
//class GC//内部类相当于友元,直接拿外类的成员{public:~GC()
//定义一个内部类,可以在main函数随处位置调用释放外部类,或者当外部类的声明周期结束时自动调用该内部类的析构函数释放外部类。而不是让创建当前外部类的父进程回收外部类{DeleteInstance();}};void Insert(const string& name, int salary){_info[name] = salary;}void Print(){for (auto& kv : _info){cout << kv.first << " : " << kv.second << endl;}cout << endl;}
private:SingalInfo() { cout << "touch SingalInfo" << endl; };//构造函数私有化SingalInfo(SingalInfo& sig) = delete;//禁用拷贝构造SingalInfo& operator=(SingalInfo& sig) = delete;//禁用拷贝赋值
private:static	SingalInfo* _sig;//类内声明---整个类只有一个对象map<string, int> _info;static mutex _mut;static GC _gc;//内部类声明};
SingalInfo* SingalInfo::_sig=nullptr;//类外定义对象,全局定义具有全局属性,声明周期是全局
mutex SingalInfo::_mut;
SingalInfo::GC SingalInfo::_gc;
int main()
{SingalInfo& kk = SingalInfo::getbody();kk.Insert("me", 10000);kk.Insert("you", 20000);kk.Insert("who", 30000);kk.Print();SingalInfo::getbody().Insert("she", 25000);SingalInfo::getbody().Print();kk.Print();//此时kk对象打印的内容和前面的匿名对象打印的内容是一样的表示全局内对象只有一份return 0;
}
  • 类内声明成员_sig是SingalInfo指针类型,且用static修饰,表示该成员属于整个类。在类外定义为全局属性,表示具有全局性,声明周期为全局。

  • _mut是互斥锁,用于保护临界区。在加锁和解锁之间的代码称为临界区。加锁后同个时间内只能有一个线程进入临界区,维持了线程安全。

  • map是一个键值对类型的数据结构,用来存储数据。

  • _gc为内部类,内部类相当于友元函数,可以调用外部类内成员DeleteInstance函数,负责释放对象 _sig和释放锁 _mut。可以不调用DeleteInstance函数进行释放资源,当该进程解锁后由父进程进行回收释放。也可在程序解锁时,内部类GC的周期结束自动调用析构函数,进而调用DeleteInstance函数进行释放资源。

  • LockGuard类负责加锁解锁工作,该类为RAII型,外部传互斥锁进来,构造函数对该锁进行加锁,析构函数对该锁进行解锁。

  • getbody函数用于获取对象。用static修饰表示可以通过类内函数进行调用。

  • getbody函数进行双重检查成员_sig==nullptr的的作用在于:第一次检查是否是第一次调用getbody函数,第一次调用说明当前还未创建对象。第二次检查用于加锁限制一个线程进入获取对象。

  • 互斥锁 _mut用LockGuard包装作用在于当退出外层if函数时自动调用LockGuard对象的析构函数,去解锁,避免了因为new失败进行抛异常导致退出当前栈帧而解锁失败的问题。

image-20230909164043447

其实在获取对象时是通过new对象,构造函数创建对象,指针接收,势必可能存在线程安全问题,因此需要加锁保护,双重检查,后续解锁释放锁资源等等工作。在C++11中,保证了创建静态对象是线程安全的,因此可以通过获取静态对象,避免线程安全问题的同时摒弃掉互斥锁等等资源的调用和释放

class SingalInfo
{
public:static SingalInfo& GetInstance(){static SingalInfo instance;//静态的局部变量是在main函数之后创建的//C++11之后能够保证创建静态对象是线程安全的return instance;}void Insert(const string& name, int salary){_info[name] = salary;}void Print(){for (auto& kv : _info){cout << kv.first << " : " << kv.second << endl;}cout << endl;}
private:SingalInfo() { cout << "touch SingalInfo" << endl; };//构造函数私有化SingalInfo(SingalInfo& sig) = delete;//禁用拷贝构造SingalInfo& operator=(SingalInfo& sig) = delete;//禁用拷贝赋值
private:map<string, int> _info;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/126871.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rocky(Centos)安装中文字体(防止中文乱码)

1、查看字体列表 运行下列命令 fc-list 若出现&#xff0c;下面截图&#xff0c;则需要安装字体管理软件 安装字体库&#xff0c;运行&#xff1a; yum -y install fontconfig 当看到下图的提示信息时说明已安装成功&#xff1a; 二、添加中文字体 1&#xff09;window…

python实现adb辅助点击屏幕工具

#!/usr/bin/env python # -*- coding: utf-8 -*-import re import os import time import subprocess import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk# 设置ADB路径&#xff08;根据你的系统和安装路径进行调整&#xff09; ADB_PATH C…

centos+jenkins+pycharm

思路&#xff1a;架构 一. 在centos上搭建jenkins环境 二. pycharm与gitee建立连接 三. 访问jenkins&#xff0c;添加任务 3.1 添加一个自由风格的任务 3.2 添加git项目路径及访问git的账号和密码 3.3 执行start.sh脚本 四. 浏览器访问jenkins执行任务

MySQL--MySQL表的增删改查(基础)

排序&#xff1a;ORDER BY 语法&#xff1a; – ASC 为升序&#xff08;从小到大&#xff09; – DESC 为降序&#xff08;从大到小&#xff09; – 默认为 ASC SELECT … FROM table_name [WHERE …] ORDER BY column [ASC|DESC], […]; *** update

半导体制造工艺(一)光刻

在这里开个新专题&#xff0c;主要详细描述半导体制造整个流程中所用到的设备工艺步骤。 在集成电路制造工艺中&#xff0c;光刻是决定集成器件集成度的核心工序&#xff0c;该工序的作用是将图形信息从掩模版&#xff08;也称掩膜版&#xff09;上保真传输、转印到半导体材料衬…

深度解析自然语言处理之篇章分析

在本文中&#xff0c;我们深入探讨了篇章分析的概念及其在自然语言处理&#xff08;NLP&#xff09;领域中的研究主题&#xff0c;以及两种先进的话语分割方法&#xff1a;基于词汇句法树的统计模型和基于BiLSTM-CRF的神经网络模型。 关注TechLead&#xff0c;分享AI全维度知识…

编译OpenWrt内核驱动

编译OpenWrt内核驱动可以参考OpenWrt内部其它驱动的编写例程&#xff0c;来修改成自己需要的驱动 一、OpenWrt源代码获取与编译 1.1、搭建环境 下载OpenWrt的官方源码&#xff1a; git clone https://github.com/openwrt/openwrt.git1.2、安装编译依赖项 sudo apt update -…

Web of Science怎么用有哪些功能

Web of Science你不可不知道的数据库。作为全球最大的学术搜索引擎之一&#xff0c;Web of Science涵盖了众多学科领域&#xff0c;为科研人员提供了全面、高品质的学术资源。本文将详细介绍Web of Science的主要功能及使用步骤&#xff0c;希望可以帮助您更好地利用这一强大的…

杭州高职画室哪家好?如何选择高职画室?高职美术学习选哪家画室?

随着越来越多的画室开始涉足高职美术培训&#xff0c;根据杭州高职画室的美术学生及其家长所知&#xff0c;由于普通高中和高职联考之间存在巨大差异&#xff0c;因此许多普通高中的画室的高职班并未取得太大的成功。因此&#xff0c;小编为正在寻找画室的你提供介绍&#xff1…

QTday5(QT连接TCP通信)

一、Xmind整理&#xff1a; C语言中的通信协议&#xff1a; 二、上课笔记整理&#xff1a; 1.QT中的服务器端的操作&#xff1a; .pro文件&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务…

设计模式 - 责任链

一、前言 ​ 相信大家平时或多或少都间接接触过责任链设计模式&#xff0c;只是可能有些同学自己不知道此处用的是该设计模式&#xff0c;比如说 Java Web 中的 Filter 过滤器&#xff0c;就是非常经典的责任链设计模式的例子。 那么什么是责任链设计模式呢&#xff1f; ​ …

Android studio 实现生成二维码和扫描二维码

效果图 build.gradle(:app)添加依赖 dependencies {implementation com.google.zxing:core:3.3.3implementation com.journeyapps:zxing-android-embedded:3.6.0implementation com.google.zxing:javase:3.0.0 }Manifests.xml <uses-permission android:name"android…

FastStone Capture

FastStone Capture 简介下载安装注册 简介 FastStone Capture是一款用于屏幕截图和屏幕录制的工具。它允许用户捕捉屏幕上的内容&#xff0c;并将其保存为图像文件&#xff0c;还可以录制屏幕活动为视频文件。 FastStone Capture官网: https://www.faststone.org/FSCaptureDet…

手写Spring:第2章-创建简单的Bean容器

文章目录 一、目标&#xff1a;创建简单的Bean容器二、设计&#xff1a;创建简单的Bean容器三、实现&#xff1a;创建简单的Bean容器3.0 引入依赖3.1 工程结构3.2 创建简单Bean容器类图3.3 Bean定义3.4 Bean工厂 四、测试&#xff1a;创建简单的Bean容器4.1 用户Bean对象4.2 单…

go-zero直连与etcd服务注册中心

go-zero中直连方式 在使用grpc是最重要的就是pb文件了&#xff0c;生成的pb文件&#xff0c;通过pb文件可以生成grpc的客户端和服务端&#xff0c;那么客户端和服务端就可以直连了&#xff0c;再次基础上可以引入etcd实现服务注册。 所有的代码都需要开发者编写&#xff0c;包…

Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作

Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 目录 Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 一、简单介绍 二、汉化操作 附录&#xff1a; 一、Install from URL 中出现 Failed to connect to 127.0.0.1 port 7890: Connection refused 错误&#xf…

基于微信小程序美食菜品预订点餐预约系统uniapp+vue

点餐预约系统主要是为了提高用户的工作效率和更方便快捷的满足用户&#xff0c;更好存储所有数据信息及快速方便的检索功能&#xff0c;对点餐预约系统的各个模块是通过许多今天的发达点餐预约系统做出合理的分析来确定考虑用户的可操作性&#xff0c;遵循开发的系统优化的原则…

网络安全概述

从今天开始&#xff0c;我将开启一个网络安全的小课堂&#xff0c;分享一些网络安全方面的相关知识点&#xff0c;大家可以一起学习&#xff0c;争取对网络安全、网络攻防有一定的认识&#xff0c;今天是第一讲网络安全概述&#xff0c;来简单介绍一下网络安全。 1 网络安全的…

数据结构零基础入门篇(C语言实现)

前言&#xff1a;数据结构属于C学习中较难的一部分&#xff0c;对应学习者的要求较高&#xff0c;如基础不扎实&#xff0c;建议着重学习C语言中的指针和结构体&#xff0c;万丈高楼平地起。 目录&#xff1a; 一&#xff0c;链表 1&#xff09;单链表的大致结构实现 2&…

IDEA插件Mybatis Log Plugin的安装及其使用教程

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 插件概述 Mybatis Log Plugin插件用于查看Mybatis所执行的完整SQL语句。在此教程中详细介绍IDEA插件Mybatis Log Plugin的安装及其使用。 安装过程 请搜索并安装Mybatis …