前言
关于类型转换的细节,这里小编和大家探讨两个方面:
- 关于类型转化的临时变量的问题
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
的理解:
ptr1
和ptr1
指向的内容都不可以更改ptr2
指向的内容不可以更改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是错误的!!
这个时候就要问为什么?
我们来分析一下:
-
首先我们来看,这个赋值的过程,来看是否有权限的放大问题。首先,创建一个
const
变量d
,然后一个创建了一个const int*
的指针指向这个变量,没有问题,是一个权限的平移。然后略过创建e,f,b
过程,来到const int **a = b
语句,从权限来看,这似乎是没有问题的,a
是一个指针的指针,对于a
指向的指针的指向内容是一个const
内容,而b
指针指向的指针的指向内容不是一个const
内容,似乎看来这是一个权限的缩小。指向是有潜在有问题的! -
我们用
b
初始化了a
,那么修改*a
,就是修改*b
,经过了语句5,此时*a
和*b
都指向了c
。发现了吗?c
是一个const int*
类型,而b
是一个int*
类型。没错,这里发生了什么?间接地使const int*
类型转换为了int*
类型(违背了上面的原则!)。即使是我们去掉了语句5,结果仍然不会通过!
-
下面我们作图来解释关系:
所以: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哪些有问题,为什么呢?
还有什么经验细节,都欢迎大家分享!
完。