当前位置: 首页 > news >正文

特殊类设计和类型转换

前言

这一篇博客我们讲特殊类设计和类型转换

1. 特殊类设计

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

这个比较简单
第一种方法就是将赋值和拷贝构造只定义不声明然后设置为私有就可以了
第二种方法就是直接令它为delete

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

class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}
private:HeapOnly(){}HeapOnly(const HeapOnly&h){}
};

只需要把构造函数和拷贝构造私有化就可以了,然后提供一个接口
注意要有一个static,不然的话无法调用

class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}void destroy(){delete this;}
private:~HeapOnly(){}
};

第二个方法就是将析构函数私有化,这样就不能正常在栈上创建对象了

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

class StackOnly
{
public:StackOnly CreateObj(){return StackOnly();}
private:StackOnly(){}StackOnly(const StackOnly&h){}
};

因为new一个对象会调用构造嘛,所以把构造函数私有,就无法new了
然后还是要提供接口去调用

1.4 请设计一个类,不能被继承

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

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

第二个方法就是将类用final修饰,表示这个类不能继承

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

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

单例模式就是这个类只有一个实例化对象,而且这个东西是可以全局访问的
单例模式又分为两种模式,一个是饿汉模式,一个是懒汉模式
饿汉模式就是在程序启动前就已经创建好这个对象了
懒汉模式就是你要用的时候才创建好这个对象
我们先讲饿汉模式

{
public:/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了A(const A& a) = delete;static A _a;int _num;
private:A(){};
};
A A::_a;//这里会调用默认构造函数

这样就可以了,构造函数私有化,拷贝构造delete,那么就只有那一个静态成员的
注意这里A中含有一个static类型的A,在这里不是套娃
static A _a;是存在静态区的,是创建的一个变量,是全局的,只不过在A中就有了类域的限制,它不是A的一个成员,而是创建的一个对象,然后static类型的,声明与定义分离
但这个是在main函数之前就定义好了,这个对象是在main函数之前就创建好了
缺点就是,在main函数之前创建,就会导致,程序启动缓慢
下面来讲懒汉模式

class A
{
public:/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了A(const A& a) = delete;static A& CreatObj(){if (_ptr == nullptr){_ptr = new A;}return *_ptr;}int _num;static A* _ptr;
private:A(){};
};
A* A::_ptr = nullptr;//main函数之前就创建了一个nullptr
A::CreatObj()._num++;
A::CreatObj()._num++;
A::CreatObj()._num++;

在这里插入图片描述
这里可以不用释放,因为只有一个对象,内存泄漏了,也没事

但是如果要释放的话,写个析构是不行的
因为static A* a是不会自动调用析构函数的
要释放的话,就要在里面写个专门释放的函数
然后再专门调用

或者写个获取_ptr的函数也行

void Destroy()
{delete _ptr;
}

比如这样
或者在类里面定义一个内部类

class CGarbo {
public:
~CGarbo(){
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};

然后在用这个定义一个静态的成员变量

static CGarbo Garbo;

static A a在生命周期结束的时候是会自动调用析构函数的
然后内部类可以用外部类的成员变量,就可以析构了

class A
{
public:/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了A(const A& a) = delete;static A& CreatObj(){if (_ptr == nullptr){_ptr = new A;}return *_ptr;}void Destroy(){delete _ptr;}class B{public:~B(){cout << "h" << endl;delete _ptr;}};int _num=0;static A* _ptr;static B b;~A(){;}
private:A(){};
};
A* A::_ptr = nullptr;//main函数之前就创建了一个nullptr
A::B A::b;

因为定义了b的static,这不是成员变量,也是单独的,所以static A a是会自己析构的

class A
{
public:/*A() = delete;*///不能删除,因为如果删除的话,就真的一个对象都没有了A(const A& a) = delete;static A& CreatObj(){static A a;return a;}int _num = 0;
private:A(){};
};

这个是懒汉模式的第二种
只有调用CreatObj函数的时候才会创建那个对象,然后这个会自动析构,不是开辟的,不用释放

反正static的都不是成员变量,而是单独的一个对象

2. C语言中的类型转换

2.1内置类型之间

隐式类型转换:整型家族之间,整型与浮点数之间
显示类型转换(也是强制类型转换):指针与整数之间,指针与指针之间

int a = 'c';//隐式类型转换

在这里插入图片描述
这个就不行,因为要强制类型转换的东西,隐式类型转换是不行的

int a = 'c';//隐式类型转换
int b = (int) & a;

这里我们就可以看出了,第一,类型转换转换的是类型比较相近的,其实隐式类型转换最多就是一些精度的丢失,数据的主体意思还是不会改变,而强制类型转换,是直接就改变了数据的意义

2.2 内置类型和自定义类型之间

class A
{
public:A(int a):_a1(a),_a2(a){}A(int a1,int a2):_a1(a1), _a2(a2){}
private:int _a1;int _a2;
};
A tmp1 = 1;
A tmp2 = {1,2};
const A& tmp3 = {1,2};

这里我们可以看出,内置类型是可以直接隐式类型转换成自定义类型的,前提就是支持相关的构造函数

A tmp1 = (A)1;

当然能隐式类型转换,肯定也能强制类型转换,两个参数的我不知道怎么强制类型转换

explicit A(int a):_a1(a),_a2(a)
{}

在这里插入图片描述

如果在构造函数的前面加上explicit就表示这个相关的隐式类型转换就不能进行

A tmp1 = (A)1;

但是强制类型转换还是可以的

接下来我们讲自定义类型隐式类型转换为内置类型

operator int()
{return _a1 + _a2;
}
int num = tmp1;
cout << num << endl;

在这里插入图片描述
在这里我们就可以看出,涉及自定义类型的转换,都是要相关的函数的
自定义隐式变内置类型,就要重载,但是这个重载比较特殊,返回值不能写,就是默认的int,能隐式肯定也能显示,就不用说了

在这里插入图片描述

shared_ptr中也有个隐式类型转换,是转换为bool值的,看shared_ptr指向的内容是否有效

2.3 自定义类型和自定义类型之间

只要有自定义类型,那么类型转换的话,就要有相应函数,直接转换肯定是不行的
这次的函数是构造函数

class B
{
public:B(int a):_b1(a), _b2(a){}B(int a1, int a2):_b1(a1), _b2(a2){}int _b1;int _b2;
};class A
{
public:A(const B& b){_a1 = b._b1;_a2 = b._b2;}explicit A(int a):_a1(a), _a2(a){}A(int a1, int a2):_a1(a1), _a2(a2){}operator int(){return _a1 + _a2;}
private:int _a1;int _a2;
};
B b1 = { 1,2 };
A a1 = b1;

这里我们就可以看出,如果要将B类型转换为A类型,那么A类型就要支持B类型的构造函数

3. C++强制类型转换

C语言中的类型转换只有内置类型与内置类型,后面两种是C++的
虽然支持隐式类型转换,但是C++为了规范,为了让别人知道是什么类型的转换
直接引入了四种强制类型转换操作符

3.1 static_cast

static_cast对应的就是以前的隐式类型转换
以前是这样

double d = 0.22;
int a = d;

现在可以是这样

	double d = 0.22;int a = static_cast<int>(d);

<>里面的内容就是要转换的内容

3.2 reinterpret_cast

reinterpret_cast就是作用于强制类型转换的
以前是这样

int a = 0;
int b = (int)&a;

现在是这样

int a = 0;
int b = reinterpret_cast<int>( & a);

还是那句话,要求强制的就不能隐式,能隐式的绝对能强制
所以按理说static_cast可以用的,reinterpret_cast也可以用
但是static_cast可以用的,reinterpret_cast也不可以用
在这里插入图片描述
因为主要是为了规范,隐式就必须用static_cast,显示就必须用reinterpret_cast
在这里插入图片描述

3.3 const_cast

const_cast主要是对const类型的进行转换,然后还可以去掉它的const属性,意思是,要去掉const属性的时候,就要用这个,
在这里插入图片描述
在这里插入图片描述
可以看出const_cast针对的类型是指针,转换为指针,而且还要具有const属性的
所以const_cast是针对指针和引用类型,转换为指针和引用类型,然后这个指针和引用类型还具有const,用了这个就可以去掉cosnt属性
前面两个都是无法去掉const属性的
在这里插入图片描述
比如这个,&a是const int类型的,使用了这个转换符还是无法转换为int,意思就是无法去掉const属性

const int a = 0;
int* d = const_cast<int*>(&a);
*d = 10;
cout << a << endl;
cout <<(*d) << endl;

这个就可以去掉const属性
在这里插入图片描述
d这个指针就不具有const属性,就可以对指向内容进行修改了
虽然进行了修改,按理说a是已经改变了的,但是const类型的,编译器有时会把它放在寄存器,有时会把它直接用2宏替换,所以用的a一直是0,并且a的const属性不会改变
虽然d指针指向a,但是也可以当做没有指向a,重新弄了一个数据为0来指向

3.4 dynamic_cast

danamic_cast主要是针对子类和父类的相互赋值的
第一子类可以直接赋值给父类,指针引用都可以赋给父类
第二但是父类的指针引用不能给子类,因为子类指向的空间更大,会越界访问
主要是越界访问的问题
所以danamic_cast针对第一种情况成功转换
第二种情况就会返回nullptr或者返回0
但是使用这个的前提就是父类要具有虚函数

class A
{
public:virtual void f() {}
};
class B : public A
{};
void fun(A* pa)
{// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回B* pb2 = dynamic_cast<B*>(pa);if (pb2){cout << "转换成功" << endl;cout << "pb2:" << pb2 << endl;}else{cout << "转换失败" << endl;}
}
int main()
{A a;B b;fun(&a);fun(&b);return 0;
}

第一个pa指向的空间是A类型的,所以转换为访问B类型的,就会越界,很危险
第二个pa指向的空间是B类型的,只不过指向的一部分,但是转换为B类型的指针没有问题,因为可以访问多余的空间

4. RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
  3. decltype

就是可以判断类型的就是RTTI

总结

下一篇博客我们讲讲linux

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 进阶SpringBoot之 SpringSecurity(2)用户认证和授权
  • TIM输出比较之PWM驱动直流电机应用案例
  • UEFI启动流程
  • 《黑神话:悟空》到底是用什么语言开发的
  • 从0到1构建视频汇聚生态:EasyCVR视频汇聚平台流媒体协议支持的前瞻性布局
  • 依靠 VPN 生存——探索 VPN 后利用技术
  • 【Python】列表和元组
  • 两个独立的SpringBoot项目如何互相引用?
  • 20240824给飞凌OK3588-C的核心板刷Ubuntu22.04后适配SONY索尼的HDMI OUT的机芯8530
  • 【通俗易懂】限流、降级、熔断有什么区别?
  • C语言刷题日记(附详解)(2)
  • 防患未然:构建AIGC时代下开发团队应对突发技术故障与危机的全面策略
  • 代码随想录算法训练营day30 | 贪心算法 | 452.用最少数量的箭引爆气球、435.无重叠区间、763.划分字母区间
  • PostgreSQL SELECT 语句:深入解析与实例应用
  • vscode修改选中文字颜色及当前tab颜色
  • 时间复杂度分析经典问题——最大子序列和
  • .pyc 想到的一些问题
  • [Vue CLI 3] 配置解析之 css.extract
  • angular组件开发
  • Django 博客开发教程 16 - 统计文章阅读量
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • java多线程
  • jQuery(一)
  • Node项目之评分系统(二)- 数据库设计
  • Ruby 2.x 源代码分析:扩展 概述
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • SSH 免密登录
  • Twitter赢在开放,三年创造奇迹
  • 规范化安全开发 KOA 手脚架
  • 基于 Babel 的 npm 包最小化设置
  • 检测对象或数组
  • 两列自适应布局方案整理
  • 深入 Nginx 之配置篇
  • 事件委托的小应用
  • 一个SAP顾问在美国的这些年
  • 自动记录MySQL慢查询快照脚本
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • 阿里云ACE认证学习知识点梳理
  • # Redis 入门到精通(一)数据类型(4)
  • #java学习笔记(面向对象)----(未完结)
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (接口自动化)Python3操作MySQL数据库
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (十六)一篇文章学会Java的常用API
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (原創) 未来三学期想要修的课 (日記)
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • (转载)Linux 多线程条件变量同步
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net 7和core版 SignalR
  • .net core 的缓存方案