关于类型转换的细节(隐式类型转换的临时变量和理解const权限)

前言

关于类型转换的细节,这里小编和大家探讨两个方面:

  1. 关于类型转化的临时变量的问题
  2. const关键字的权限问题 — 即修改权限。小编或通过一道例题(配图)来带大家了解这个权限问题!

类型转换的细节

还有一些常见的问题(例如整型提升的问题)小编这里不做介绍,主要介绍下面两种:

1. 类型转换的临时变量

为什么说类型转换细节呢?来看下面的程序:

#include<iostram>
using namespace std;
int main()
{
    int a = 0;
    double& ref = a;
    return 0;
}

又或者是这样:

#include<iostram>
using namespace std;
int main()
{
    int a = 0;
    double& ref = (double)a;
    return 0;
}

上面代码有问题吗?当然有问题,VS2022编译器是会报错的……

在这里插入图片描述

这是什么原因导致的呢?

  • 实际上一个类型转换的过程应该是这样的:
    在这里插入图片描述
    临时变量tmp是具有常性的(可读不可写),所以是无法如此被引用的。所以只能通过另外的方法,得到它们的引用。

注:关于这个观点证明,借助右值引用的移动语义就可以完成验证。

#include<iostram>
using namespace std;
int main()
{
    int a = 0;
    const double& ref = a;
    return 0;
}
  • 一定是const type&才可以。一定要注意这个细节。
  • 关于对于这个问题,编译器在对自定义类型的时候都会做出一定的优化

那么对于这些场景就会产生许多问题,例:

namespace test
{
    class A
    {
    public:
        A()
            :_a(1)
        {}
        virtual void set(int val)
        {
            _a = val;
        }
        int _a;
    };

    class B :public A
    {
    public:
        B()
            :_b(0)
        {}
        virtual void set(int val)
        {
            static_cast<A>(*this).set(val); //很有问题的写法
            //A::set(val);
        }
        int _b;
    };

    void Test1()
    {
        B bb;
        bb.set(120);
        cout << bb._a << endl;
    }
}

我们想要在B类的set中调用A类(父类)的set,但是这样的类型转换是不会起到任何效果的,还记得刚刚所讨论的吗?类型转换会产生临时变量临时变量是不会改变原来的值的。这些都是隐式类型转换的细节。

包括有时候对指针进行了强制类型转换过后:

int a = 10;
int* ptr = &a;
char* c = ++(char*)ptr;

这样的类型转换都是不会达到如愿的结果的!!!

细节二:const与指针

在说明这个问题之前,我们先声明:

  • 权限可以缩小
  • 权限可以平移
  • 权限不能放大

来看下面这个例子,来理解一些关于权限问题

const int* const ptr1 = 0;
const int* ptr2 = 1;
int* const ptr3 = 2;

对于上面三个语句,以我们对于const的理解:

  1. ptr1ptr1指向的内容都不可以更改
  2. ptr2指向的内容不可以更改
  3. ptr3本身不能更改

我们都知道:const T* 是不允许转换为T*。(除非使用const_cast去掉const属性)这些都是我们所了解的,那么如果加上二级指针呢?
例如下面代码:

const int d = 0;   
const int* c = &d;   //1
int e = 1;   
int *f = &e;        //2
int **b = &f;       //3
const int **a = b; //4
*a = c;             //5

对于上面所标识的5条语句,你觉得有没有错误的呢?

编译器会告诉你,语句4是错误的!!
在这里插入图片描述
这个时候就要问为什么

我们来分析一下:

  1. 首先我们来看,这个赋值的过程,来看是否有权限的放大问题。首先,创建一个const变量d,然后一个创建了一个const int*的指针指向这个变量,没有问题,是一个权限的平移。然后略过创建e,f,b过程,来到const int **a = b语句,从权限来看,这似乎是没有问题的,a是一个指针的指针,对于a指向的指针的指向内容一个const内容,而b指针指向的指针的指向内容不是一个const内容,似乎看来这是一个权限的缩小。指向是有潜在有问题的!

  2. 我们用b初始化了a,那么修改*a,就是修改*b,经过了语句5,此时*a*b都指向了c。发现了吗?c是一个const int* 类型,而b是一个int* 类型。没错,这里发生了什么?间接地使const int*类型转换为了int*类型(违背了上面的原则!)。即使是我们去掉了语句5,结果仍然不会通过!
    在这里插入图片描述

  3. 下面我们作图来解释关系
    在这里插入图片描述

所以:int ** 不能转换为 const int ** !!!这是为了以绝后患。如果在const int **声明的时候做如下声明:const int* const *a = b 那么语句*a = c就不会通过编译,就不会出现报错的问题了!
在这里插入图片描述
那么有了以上的一些储备,我们来看如下的代码:

class A{};

void f(const A** p){}
void g(const A* const *p){}
void k(const A*& p){}

int main()
{
	const A* ca = new A;
	A* a = new A;
	A** p = &a;
	k(ca); // 1
	f(p);  // 2
	g(p);  // 3

	// ……
	return 0;
}

在这个代码中,你能看出来语句1,2,3哪些有问题,为什么呢?

还有什么经验细节,都欢迎大家分享!

完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值