(P57)面向对象版表达式计算器:单例模式与auto_ptr

2024-06-08 05:58

本文主要是介绍(P57)面向对象版表达式计算器:单例模式与auto_ptr,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1.auto_ptr在单例模式中的应用

1.auto_ptr在单例模式中的应用

  • eg:P57\01.cpp
#include <iostream>
#include <memory>
using namespace std;//单例模式是保证一个类在一个程序中只有一个对象,一个实例
//实现单例模式的关键点是:对象禁止拷贝(只需将拷贝构造函数与等号运算符声明为私有的,且并提供它的实现,这样的话,编译会报错),此外将
//构造函数声明为私有的,防止外部任意构造对象,所以需要提供一个接口ingleton::GetInstance();让外部得到这样一个对象
//因为无法通过对象调用成员函数,所以只能通过类调用成员函数,所以将GetInstance声明为static,此外,不论调用多少次GetInstance,返回的是同一个对象
//否则一个程序就有多个对象的拷贝
class Singleton
{
public://GetInstance成员函数可以访问Singleton私有的构造函数static Singleton* GetInstance(){if (instacne_ == NULL){instacne_ = new Singleton;//new:创建一个类的实例}return instacne_;}~Singleton(){cout<<"~Singleton ..."<<endl;}
private://禁止拷贝就是将拷贝构造函数,等号运算符声明为私有的,就可以保证不进行拷贝构造,也不能赋值//禁止拷贝1:禁止调用拷贝构造函数//将拷贝构造函数声明为私有的,且不提供她的实现Singleton(const Singleton& other);//禁止拷贝2://将赋值操作声明为私有的,禁止赋值操作Singleton& operator=(const Singleton& other);//将构造函数声明为私有的,在main函数中就不能调用构造函数Singleton(){cout<<"Singleton ..."<<endl;}//解决办法是使用智能指针,当智能指针生命周期结束的时候,会自动调用析构函数,从而释放指针所持有(指向)的资源//所以智能指针能用在单例模式中,对资源进行释放static Singleton* instacne_;//仅仅声明,这是裸指针,用智能指针管理// static shared_ptr<Singleton> instacne_;//当整个程序结束时,静态对象也会被销毁,就会调用这个shared_ptr类的析构函数,析构就会将其持有的指针资源进行释放
};Singleton* Singleton::instacne_;//定义性的说明int main(void)
{//Singleton s1;//Singleton s2;//不论调用几次GetInstance,总是返回同一个对象,同一个实例//如何验证他们是同一个实例?只需查看s1和s2指针所指向的地址是否一致Singleton* s1 = Singleton::GetInstance();Singleton* s2 = Singleton::GetInstance();//Singleton s3(*s1);		// 调用拷贝构造函数//s4=s2return 0;
}
  • 测试:new Singleton这个对象未被销毁,析构函数未调用。这个指针static Singleton* instacne_;未被销毁
    在这里插入图片描述

  • eg:P57\01_share_ptr.cpp

#include <iostream>
#include <memory>
using namespace std;//单例模式是保证一个类在一个程序中只有一个对象,一个实例
//实现单例模式的关键点是:对象禁止拷贝(只需将拷贝构造函数与等号运算符声明为私有的,且并提供它的实现,这样的话,编译会报错),此外将
//构造函数声明为私有的,防止外部任意构造对象,所以需要提供一个接口ingleton::GetInstance();让外部得到这样一个对象
//因为无法通过对象调用成员函数,所以只能通过类调用成员函数,所以将GetInstance声明为static,此外,不论调用多少次GetInstance,返回的是同一个对象
//否则一个程序就有多个对象的拷贝
class Singleton
{
public://GetInstance成员函数可以访问Singleton私有的构造函数static Singleton* GetInstance(){// if (instacne_ == NULL)// {// 	instacne_ = new Singleton;//new:创建一个类的实例// }// return instacne_;//get()方法返回原生指针,但没有释放所有权,release()方法会释放所有权if (!instacne_.get())){instacne_ = auto_ptr<Singleton>(new Singleton);//智能指针的所有权转移给了instacne_,instacne_持有了new Singleton所分配的内存资源}return instacne_.get();}~Singleton(){cout<<"~Singleton ..."<<endl;}
private://禁止拷贝就是将拷贝构造函数,等号运算符声明为私有的,就可以保证不进行拷贝构造,也不能赋值//禁止拷贝1:禁止调用拷贝构造函数//将拷贝构造函数声明为私有的,且不提供她的实现Singleton(const Singleton& other);//禁止拷贝2://将赋值操作声明为私有的,禁止赋值操作Singleton& operator=(const Singleton& other);//将构造函数声明为私有的,在main函数中就不能调用构造函数Singleton(){cout<<"Singleton ..."<<endl;}//解决办法是使用智能指针,当智能指针生命周期结束的时候,会自动调用析构函数,从而释放指针所持有(指向)的资源//所以智能指针能用在单例模式中,对资源进行释放// static Singleton* instacne_;//仅仅声明,这是裸指针,用智能指针管理static shared_ptr<Singleton> instacne_;//当整个程序结束时,静态对象也会被销毁,就会调用这个shared_ptr类的析构函数,析构就会将其持有的指针资源进行释放
};// Singleton* Singleton::instacne_;//定义性的说明
shared_ptr<Singleton> Singleton::instacne_;int main(void)
{//Singleton s1;//Singleton s2;//不论调用几次GetInstance,总是返回同一个对象,同一个实例//如何验证他们是同一个实例?只需查看s1和s2指针所指向的地址是否一致Singleton* s1 = Singleton::GetInstance();Singleton* s2 = Singleton::GetInstance();//Singleton s3(*s1);		// 调用拷贝构造函数//s4=s2return 0;
}
  • 测试:析构函数调用了,说明new Singleton这块资源就能够自动释放了
    在这里插入图片描述

  • eg:P57\02.cpp

#include <iostream>
#include <memory>
using namespace std;//为什么Noncopyable类是禁止拷贝的?
//(1)【采用】禁止对象拷贝的eg演示
//Noncopyable不能构造对象,因为构造对象没意义,仅仅用来继承
class Noncopyable
{
protected:Noncopyable() {};~Noncopyable() {};
private:Noncopyable(const Noncopyable&);const Noncopyable& operator=(const Noncopyable&);
};//这里继承方式是private的,why?
//这里仅仅是继承它的实现,是继承它的实现,而不是继承它的接口,所以是实现继承,而不是接口继承
class Parent : private Noncopyable
{};//Parent是禁止拷贝的,其子类Child也是禁止拷贝的,因为它也继承了Noncopyable的实现
class Child : public Parent 
{};int main(void)
{Parent p1;Parent p2(p1);//要调用Parent拷贝构造函数,Parent构造函数又调用Noncopyable的拷贝构造函数//基类的私有成员在派生类中是不能被访问的,所以编译会失败,即使能访问,基类也没//给出实现,同样也会出错Child c1;Child c2(c1);return 0;
}
  • 测试:也是禁止拷贝的
    在这里插入图片描述

  • eg:P57\03.cpp

#include <iostream>
#include <memory>
using namespace std;//为什么Noncopyable类是禁止拷贝的?
//(1)【采用】禁止对象拷贝的eg演示
//Noncopyable不能构造对象,因为构造对象没意义,仅仅用来继承
class Noncopyable
{
protected:Noncopyable() {};~Noncopyable() {};
private:Noncopyable(const Noncopyable&);const Noncopyable& operator=(const Noncopyable&);
};//这里继承方式是private的,why?
//这里仅仅是继承它的实现,是继承它的实现,而不是继承它的接口,所以是实现继承,而不是接口继承
class Parent : private Noncopyable
{
public://构造函数会调用基类的构造函数,即使没有写出Parent(): Noncopyable(),也是会自动调用的Parent()//若基类没有默认构造函数,那么在子类的成员函数列表中给出基类构造函数的调用{}//拷贝构造函数会调用基类的拷贝构造函数,但是只有Parent(const Parent& other) : Noncopyable(other)这么去写//才会调用基类的拷贝构造函数的Parent(const Parent& other) : Noncopyable(other){}
};//Parent是禁止拷贝的,其子类Child也是禁止拷贝的,因为它也继承了Noncopyable的实现
class Child : public Parent 
{};int main(void)
{Parent p1;//调用构造函数//调用拷贝构造函数,如果这么写:Parent(const Parent& other){},//Parent中实现了拷贝构造函数,是不会调用基类的拷贝构造函数,那么私有的Noncopyable(const Noncopyable&);这个限制就不生效了Parent p2(p1);//要调用Parent拷贝构造函数,Parent构造函数又调用Noncopyable的拷贝构造函数//基类的私有成员在派生类中是不能被访问的,所以编译会失败,即使能访问,基类也没//给出实现,同样也会出错// Child c1;// Child c2(c1);return 0;
}
  • 测试:
    在这里插入图片描述
  • eg:P57\04.cpp
#include <iostream>
using namespace std;//用宏来实现计算某个变量的大小
//sizeof_v
//&x+1,x变量的地址+1,也就是偏移了1个元素的地址,如果元素是4个字节就偏移4个字节
//&x+1 - &x,地址相减,实际上都是指针类型,两个指针相减得到的是他们之间的偏移量,相隔几个元素
//char*类型的地址相隔几个元素就是相隔几个字节
#define sizeof_v(x) (char*)(&x+1) - (char*)&x//使用宏来计算某个类型的大小
//sizeof_t
//((t*)0+1),t*的指针类型,偏移+1,就是偏移一个元素的t类型,得到地址的值刚好等于t类型的大小,但是得到的值类型还是一个指针
//size_t强制转换为整数
#define sizeof_t(t) (sizeof_t)((t*)0+1)//实现一个对齐的宏
//b必须是2的n次方
/*
eg:ALIGN(3, 16) 
v=3
b=16
写成二进制:
v=0011
b-1=1111,b减完1以后就是全1的
~(b-1)取反就是全0(v+b-1)就是:
v       0011
b-1     1111
=      10010(v+b-1) & ~(b-1)就是:100100000   将后面4位都抹除掉了
=   10000      
10000=16
原理思想:某个数要对齐到16的整数倍,用二进制表示的话,后面就是4个0,0000(用十进制去想,100的十进制后面有2个0)
32的整数倍,后面就是5个0
3对齐到16使用的是向上对齐,3 + 15肯定是超过16,超出的部分抹除掉,对于16而言,就是将低4位都置为0,所以得到的值就是对齐的值ALIGN(0, 16) = 0
ALIGN(16, 16) = 0
100001111
11111
*/
#define ALIGN(v, b)  ((v+b-1) & ~(b-1))//为什么要对齐宏ALIGN?因为内存池可能会使用到
/*
比如要分配3个字节,7个字节,9个字节。内存池中的内存块大小是规则的,规则的不容易产生内存碎片。(不规则的物品放在箱子里面会产生空隙或者内存碎片)
要分配内存就无法得到连续的空间,就会被浪费掉。
内存块可以是4K,16K大小的块,32K大小的块。
如果申请内存<=4K,就对齐到4K,保证申请的内存总是4K
如果是4K<内存<=16K,就对齐到16K
如果是16K<内存<=32K,就对齐到32K
采用分级的机制实现内存池
*/class Empty
{};int main(void)
{Empty e;int n;// cout << sizeof(e)<<endl;// cout<<sizeof(Empty)<<endl;//空类的大小是一个字节cout<<sizeof_v(e)<<endl;cout<<sizeof_v(n)<<endl;cout<<sizeof_t(Empty)<<endl;cout<<sizeof_t(int)<<endl;//3对齐到16的整数倍,应该是16cout<<ALLGN(3,16)<<endl;//应该是32cout<<ALLGN(31,16)<<endl;//应该是0cout<<ALLGN(0,16)<<endl;//应该是8192cout<<ALLGN(4198,4096)<<endl;return 0;
}
  • 测试:
    在这里插入图片描述

这篇关于(P57)面向对象版表达式计算器:单例模式与auto_ptr的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041371

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

SpringBoot @Scheduled Cron表达式使用方式

《SpringBoot@ScheduledCron表达式使用方式》:本文主要介绍SpringBoot@ScheduledCron表达式使用方式,具有很好的参考价值,希望对大家有所帮助,如有... 目录Cron 表达式详解1. 表达式格式‌2. 特殊字符解析3. 常用示例‌4. 重点规则5. 动态与复杂场景‌

Spring Boot 集成 Quartz 使用Cron 表达式实现定时任务

《SpringBoot集成Quartz使用Cron表达式实现定时任务》本文介绍了如何在SpringBoot项目中集成Quartz并使用Cron表达式进行任务调度,通过添加Quartz依赖、创... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启

Spring Boot中定时任务Cron表达式的终极指南最佳实践记录

《SpringBoot中定时任务Cron表达式的终极指南最佳实践记录》本文详细介绍了SpringBoot中定时任务的实现方法,特别是Cron表达式的使用技巧和高级用法,从基础语法到复杂场景,从快速启... 目录一、Cron表达式基础1.1 Cron表达式结构1.2 核心语法规则二、Spring Boot中定

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

用Java打造简易计算器的实现步骤

《用Java打造简易计算器的实现步骤》:本文主要介绍如何设计和实现一个简单的Java命令行计算器程序,该程序能够执行基本的数学运算(加、减、乘、除),文中通过代码介绍的非常详细,需要的朋友可以参考... 目录目标:一、项目概述与功能规划二、代码实现步骤三、测试与优化四、总结与收获总结目标:简单计算器,设计