(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

相关文章

使用Python实现一个简易计算器的新手指南

《使用Python实现一个简易计算器的新手指南》计算器是编程入门的经典项目,它涵盖了变量、输入输出、条件判断等核心编程概念,通过这个小项目,可以快速掌握Python的基础语法,并为后续更复杂的项目打下... 目录准备工作基础概念解析分步实现计算器第一步:获取用户输入第二步:实现基本运算第三步:显示计算结果进

C#和Unity中的中介者模式使用方式

《C#和Unity中的中介者模式使用方式》中介者模式通过中介者封装对象交互,降低耦合度,集中控制逻辑,适用于复杂系统组件交互场景,C#中可用事件、委托或MediatR实现,提升可维护性与灵活性... 目录C#中的中介者模式详解一、中介者模式的基本概念1. 定义2. 组成要素3. 模式结构二、中介者模式的特点

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

MySQL 设置AUTO_INCREMENT 无效的问题解决

《MySQL设置AUTO_INCREMENT无效的问题解决》本文主要介绍了MySQL设置AUTO_INCREMENT无效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录快速设置mysql的auto_increment参数一、修改 AUTO_INCREMENT 的值。

Java Lambda表达式的使用详解

《JavaLambda表达式的使用详解》:本文主要介绍JavaLambda表达式的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言二、Lambda表达式概述1. 什么是Lambda表达式?三、Lambda表达式的语法规则1. 无参数的Lambda表

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3