类型转换(2)

news/2024/11/24 20:13:37/

类型转换

  • 知识回顾
    • static_cast
    • const_cast
    • reinterpret_cast
  • 类型转换
    • dynamic_cast
    • 动态转换和静态转换区别
    • 动态转换的使用

知识回顾

static_cast

静态转换应用范围:

  • 基本数据类型的转换,但不能实现基本数据类型指针的转化,但是可以将无类型转成其他类型
  • 也可以将整型转换成枚举类型
  • 可以将左值转成右值
  • 可以实现上行转换,也就是将派生类指针转换成基类指针
class PtrInt
{
private:int* pval; // new .heap ; stack . data;
public:PtrInt(int* p = nullptr) :pval(p){}~PtrInt(){delete pval;pval = nullptr;}PtrInt(const PtrInt& it) :pval(new int(0)){*pval = *it.pval;//*pval = it.*pval;}PtrInt& operator=(const PtrInt& it){if (this != &it){delete pval;pval = new int(*it.pval);//pval = it.pval;}return *this;}PtrInt (PtrInt&& it) :pval(it.pval){it.pval = nullptr;}PtrInt& operator=(PtrInt&& it){if (this != &it){delete[]pval;pval = it.pval;it.pval = nullptr;}return *this;}
};PtrInt func(int x) {PtrInt tmp(new int(x));return tmp;
}int main() {PtrInt a(new int(10));a = func(100);return 0;
}

首先创建a对象,然后进入func函数,在函数中创建了一个tmp对象,我们知道函数内部创建的局部对象会在函数结束时进行释放。所以我们创建的tmp对象最终会被释放掉,所以呢在return结束前会生成一个将亡值,将亡值的指针指向我们tmp对象的堆内存,然后释放tmp对象时就不会释放堆内存,最后func函数返回的将亡值调用移动赋值来对a对象进行赋值。如果在tmp对象前加上const,编译器就不会移动他的资源,如果加上static关键字,其就会在数据区,不会进行移动资源。
在这里插入图片描述

const_cast

去常性转换,强制转换的目标类型必须是指针或引用。

int main() {const int a = 10;int& b = const_cast<int&>(a);b = 100;cout << "a:" << a << endl;cout << "b:" << b << endl;
}

思考上面代码输出结果是什么?输出结果a为10,b为100,有的同学可能回想b是a的引用,b的值改成了100,为什么a的值不是100呢?事实上,在运行时a的值的确被赋值成了100,也就是说a的值在运行时同b一样都是一百,但是常变量a在编译时就会进行替换,将a的值替换成10,所以输出为10。

reinterpret_cast

重新解释,其类似于强转,但是只适应于指针或引用的转换,不适用于值的转换。

int main() {int a = 0x61626364;char* cp = reinterpret_cast<char*>(&a);cout << *cp << endl;cout << *(cp+1) << endl;cout << *(cp + 2) << endl;cout << *(cp + 3) << endl;return 0;
}

同样观察上面代码思考其输出结果?
其输出结果是的d,c,b,a,0x61626364,我们的计算机是小端存储,所以地址存放低位,高地址存放高位,cp为低地址,存放16进制的64,而16进制的64转换成10进制就是100,正好是字母d的ASK码值,然后依次移动1字节,也就是63,62,61对应cba,对应输出结果是dcba。

类型转换

dynamic_cast

用法:dynamic_cast<type_name>(expression)
动态转换允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全的转换类型,把基类指针转换成派生类指针,或者把指向基类的左值转化成派生类的引用。
必须是公有继承,基类要有虚函数
特点:

  • C++其他三种类型转换均在编译时,而dynamic_cast在运行时执行的类型转换。
  • 如果对指针类型dynamic_cast转换失败,返回结果为nullptr。
  • 如果针对引用类型的dynamic_cast失败,则会抛出异常。
  • 在类的层次结构进行上行转换时,dynamic_cast和static_cast的效果是一样的。
  • 进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class Object
{
private:int value;
public:Object(int x = 0) :value(x) {}virtual void func() { cout << "value: " << value << endl; }
};
class Base : public Object
{
private:int num;
public:Base(int x = 0) :Object(x + 10), num(x) {}virtual void func() { cout << "num: " << num << endl; }
};
int main() {Base base(100);Object obj(10);Object* obja = (Object*)&base;Object* objb = static_cast<Object*>(&base);Object* objc = dynamic_cast<Object*>(&base);Object& oba = (Object&)base;Object& obb = static_cast<Object&>(base);Object& obc = dynamic_cast<Object&>(base);Object oca = (Object)base;Object ocb = static_cast<Object>(base);Object occ = dynamic_cast<Object>(base);//errObject&& oc = dynamic_cast<Object&&>(base);}

运行上面一段代码就可以应正我们的结论,在指针和引用转换的前提下进行上行转换,动态转换和静态转换都是一样的,但是在值转换的时候,动态转换就会报错,但可以将派生类对象转换为基类右值引用。(前提是继承关系,有虚函数)这也就是编译时转换。

int main() {Base base(10);void* vp = dynamic_cast<void*>(&base);Base* bp = dynamic_cast<Base*>(&vp);return 0;
}

通过这段代码我们可以发现动态转化可以将有类型指着转换成无类型指针,不可以将无类型指针转换成有类型指针。

动态转换和静态转换区别

仍然是上面的类和函数,执行下面一段代码

int main() {Base base(10);Object obj(20);Object* op = &obj;//Object* op=&base;Base* bp = dynamic_cast<Base*>(op);bp->func();Base* dp = static_cast<Base*>(op);dp->func();
}

我们可以分析上面代码,当基类指针指向派生类对象时,然后将其强转成派生类指针,然后调用func虚函数很显然其调用的是派生类的虚函数,因为其查找虚表时的虚表指针指向Base的虚表。静态转换和静态转换在此时结果是一样的,但是将op指向obj对象时,将op强转成Base指针类型就会出现强转错误导致bp指针为nullptr,不能执行func函数,静态转换可以运行并且输出基类的func函数。
这是什么原因呢?是这样的,因为静态转换是在编译时就确定了绑定关系,所以dp不管指针指向基类对象还是派生类对象都会调用op指向的的对象的虚表来查找调用函数。而动态转换会根据虚表指针来动态查找虚表的RTTI,找其基类类型,如果其基类没有找到也就是说不能把基类指针赋值给派生类指针,就会返回nullptr。所以也就不能调用func函数。在这里插入图片描述
动态转换通过虚表指针查看虚表,通过RTTI(运行时识别,存在一个指针)来查找信息,信息中存在其类型名,基类指针,派生类指针。
我们使用动态转换在基类的虚表中查找其Base类型是查不到的所以返回nullptr,而把派生类指针转换成基类指针就可以通过基类指针找到Obj类型名从而实现动态转换。

动态转换的使用


class Goods
{float _weight;  // 重量
public:Goods(float wt) : _weight(wt) {}virtual ~Goods() { std::cout << "~Trash()" << std::endl; }float GetWeight() const { return _weight; }virtual float GetPrice() const = 0;  // 价格
};// 铜
class Copper : public Goods
{static float price;
public:Copper(float wt) : Goods(wt) {}float GetPrice() const { return price; }static void SetPrice(float newprice) {price = newprice;}
};
float Copper::price = 2.80;// 纸张
class Paper : public Goods
{static float price;
public:Paper(float wt) : Goods(wt) {}float GetPrice() const { return price; }static void SetPrice(float newprice) {price = newprice;}
};
float Paper::price = 0.20;// 玻璃
class Glass : public Goods
{static float price;
public:Glass(float wt) : Goods(wt) {}float GetPrice() const { return price; }static void SetPrice(float newprice) {price = newprice;}
};
float Glass::price = 0.32;template<class Container >
void sumPrice(Container& bin)
{float total = 0;for (auto p : bin){//cout << typeid(x).name() << endl;total += p->GetPrice() * p->GetWeight();cout << "wight of : " << typeid(*p).name() << " = " << p->GetWeight() << endl;}cout << "Total price = " << total << endl;
}int main()
{srand(time(0)); // Seed the random number generatorvector<Goods*> bin;int n = rand() % 100;  // 0 99for (int i = 0; i < n; i++){switch (rand() % 3){case 0:bin.push_back(new Copper((rand() % 1000) / 10.0));break;case 1:bin.push_back(new Paper((rand() % 1000) / 10.0));break;case 2:bin.push_back(new Glass((rand() % 1000) / 10.0));break;}}// Note: bins hold exact type of object, not base type:vector<Glass*> glassBin;vector<Paper*> paperBin;vector<Copper*> coppBin;// Sort the Trashfor(auto xp:bin){Glass* gp = dynamic_cast<Glass*>(xp);Paper* pp = dynamic_cast<Paper*>(xp);Copper* cp = dynamic_cast<Copper*>(xp);if (gp != nullptr) {glassBin.push_back(gp);}else if (pp != nullptr) {paperBin.push_back(pp);}else if (cp != nullptr) {coppBin.push_back(cp);}}sumPrice(coppBin);sumPrice(paperBin);sumPrice(glassBin);sumPrice(bin);for (auto& x : bin){delete x;x = nullptr;}
}

看上面代码,可以看出设置了一个抽象类,还有三个派生类,铜,玻璃,纸张。最后存在一个模板类型的函数来计算总价格。
主函数中设置了随机数材料总数,其三类材料也用了随机值,有一个Goods指针类型的容器,每创建一个类就将其放入容器中,然后设置三个材料类型的指针容器,将不同材料放入不同容器,最后计算价格。这段代码重点在于

for(auto xp:bin){Glass* gp = dynamic_cast<Glass*>(xp);Paper* pp = dynamic_cast<Paper*>(xp);Copper* cp = dynamic_cast<Copper*>(xp);if (gp != nullptr) {glassBin.push_back(gp);}else if (pp != nullptr) {paperBin.push_back(pp);}else if (cp != nullptr) {coppBin.push_back(cp);}}

很显然我们用了动态转换来查找虚表判断其类型,如果类型相匹配便返回派生类的指针类型,不匹配则返回nullptr,通过判断返回值来判断其类型放入不同容器进行材料分类。


http://www.ppmy.cn/news/92571.html

相关文章

同城跑腿系统源码,点对点配送,帮你省时省力

随着互联网的发展&#xff0c;越来越多的人开始依赖网络来解决生活中的各种问题。而同城跑腿系统就是其中一个受欢迎的解决方案。 ​同城跑腿系统是指一种基于互联网的服务&#xff0c;通过在线平台将用户和服务提供者连接起来&#xff0c;以便用户可以轻松地安排他们的日常任务…

Python实战基础10-正则表达式

1、正则表达式 在处理字符串时&#xff0c;经常会有查找符合某些复杂规则的字符串需求。正则表达式就算用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 1.1 行定位符 行定位符就是用来描述字符串的边界&#xff0c;“A”表示行的开始&…

mysql45讲笔记

不一定要都学&#xff0c;有些感觉用不到&#xff0c;有选择的学&#xff01;&#xff01;&#xff01; 文章目录 mysql45讲1.mysql基础架构2.mysql日志系统3.事务隔离4.索引类型1.哈希表2.有序数组3.二叉搜索树4.B 树 5.索引重点概念覆盖索引索引下推最左前缀原则 6.全局锁表级…

关于 html2canvas 截图后safari报 SecurityError: The operation is insecure.的问题

html2canvas canvas.toDataURL safari canvas.toDataURL 后 SecurityError: The operation is insecure. 报错 是否加载了跨域图片&#xff0c;如果是 html2canvas 报错可以 配置下 useCORS: trueallowTaint: true 如果是为了展示图片 配置图片跨域 var img new Image();img…

Allure在本地不安装allure服务的情况下打开Allure Html报告

前言 我们使用pytestallure生成Allure测试报告后&#xff0c;需要发给领导查看报告的详细信息。此时我们通过将allure生成的html报告压缩成压缩包后发送给领导&#xff0c;但是领导电脑由于没有安装Allure服务&#xff0c;打开会全部显示“Loading”&#xff0c; 无法查看到报…

经典 SQL 数据库笔试题及答案整理

马上又是金三银四啦&#xff0c;有蛮多小伙伴在跳槽找工作&#xff0c;但对于年限稍短的软件测试工程师&#xff0c;难免会需要进行笔试&#xff0c;而在笔试中&#xff0c;基本都会碰到一道关于数据库的大题&#xff0c;今天这篇文章呢&#xff0c;就收录了下最近学员反馈上来…

MyBatis快速入门

目录 一、什么是MyBatis 二、MyBatis的学习要领 三、搭建第一个MyBatis 3.1 创建数据库和表 3.2 添加MyBatis框架支持 3.2.1 老项目添加MyBatis 3.2.2 新项目去添加MyBatis 3.3 设置MyBatis配置信息 3.3.1 设置数据库连接的相关信息 3.3.2 设置MyBatis xml保存路径 和…

实时频谱分析-2.3实时频谱分析

实时频谱分析 频谱分析要想归入实时类别中&#xff0c;必须没有间隙地、不确定地处理关心的频宽内包含的所有信息。RSA 必须获得时域波形中包含的全部信息&#xff0c;把信息转换成频域信号。实时完成这一点必须满足多个重要的信号处理要求&#xff1a; 1&#xff09;提供足够…