? ? ? ? 当表达式中出现了多种类型数据的混合运算时,往往需要进行类型转换。表达式中的类型转换分为两种:隐式转换和显式转换,但此处仅对隐式转换进行总结。
? ? ? ? 隐式转换分为算术转换和其它隐式类型转换两大类,其它隐式类型转换又主要包含数组转换为指针、指针的转换、布尔类型转换、常量转换等[1]。其中,在算术运算和关系运算中如果参与运算的操作数类型不一致,编译系统会自动对数据进行转换(隐式转换),此转换基本原则为低类型数据转换为高类型数据[2]。
1 隐式转换
1.1算术转换
? ? ? ? ①若运算对象同为有符号类型或无符号类型,低类型数据转换为高类型数据。类型高低顺序为(由低至高):char-short-int-unsigned-long-unsigned long-float-double。
? ? ? ? ②若一个运算对象是无符号类型、另一个运算对象是有符号类型,而且其中的无符号类型不小于有符号类型,那么有符号的运算对象转换为无符号的。需要注意的是,若有符号类型是负数,则会依据“趋负无穷取模”将负数转换为正数。
? ? ? ? ③基于②,若有符号类型大于无符号类型,此时转换依赖于机器对类型的表示字节数,,如int、long。
? ? ? ? 【注:ISO C++标准并没有明确规定每种数据类型的字节数和取值范围,它只是规定它们之间的字节数大小顺序满足:(signed/unsigned)char ≤?(unsigned)short ≤?(unsigned)int ≤?(unsigned)long】
1.2其它隐式类型转换
1.2.1 数组转换为指针
????????在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针,如函数形参表达等。但是,数组引用、当数组被用作decltype关键字的参数、作用于取地址符&、作用于sizeof以及作用于typeid时,上述隐式转换不会发生。
1.2.2 指针的转换
? ? ? ? 常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换为void*;指向任意对象的指针能转换为const void*。
1.2.3 布尔类型转换
? ? ? ? 如果指针或算术类型的值为0,转换结果为false;否则转换结果为true。
1.2.4 常量转换
? ? ? ? 允许将指向非常量类型的指针转换为指向相应的常量类型的指针,对于引用也一样。
? ? ? ? 自动对象:对于普通局部变量对应的对象来说,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。我们把只存在于块执行期间的对象称为自动对象(“块”指的是“{}”内含的程序块)。
? ? ? ? 局部静态对象:局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在函数结束执行也不会对它有影响(创建局部静态对象,即将局部变量定义成static类型)。
? ? ? ? 函数的参数传递:①形参的类型决定了形参和实参交互的方式。如果形参是引用类型,它将绑定到对应的实参上,此时对应的实参被引用传递或者函数被传引用调用;否则,当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象,此时实参被值传递或者函数被传值调用。(需要注意的是,数组不允许拷贝,无法以值传递方式使用数组参数)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ②当用实参初始化形参时会忽略掉顶层const。即,当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。
? ? ? ? 函数重载:如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。对于重载的函数来说,它们应该在形参数量或形参类型上有所不同(不允许两个函数除了返回类型外其它所有的要素都相同)。
? ? ? ? 内联函数:请求编译器在可能的情况下在调用点展开函数(定义内联函数,即在函数前添加inline标识)。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。(补充理解:调用函数一般比求等价表达式的值要慢一些。在大多数机器上,一次函数调用其实包含着一系列工作)
? ? ? ? 函数指针:函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
? ? ? ? tuple类型(标准库特殊类型):tuple是类似于pair的模板。不同tuple类型的成员类型也不相同,但一个tuple可以有任意数量的成员。每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同。tuple的主要使用目的是将一些数据组合成单一对象,而避免定义新数据结构来表示。
实验基础代码:
# include<iostream>
# include<string>
using namespace std;
// 打印出字母串内包含的大写字母,并将大写字母改写为小写字母
// 注:该展示原始代码报错,左值*b不可修改
void ToUpper(const string &str)
{
for (auto b = str.begin(); b != str.end(); ++b)
if (isupper(*b))
{
cout << *b << endl;
*b = tolower(*b);
}
return;
}
int main()
{
const string test_string = "AppLe";
cout << "原始测试数据:AppLe" << endl;
ToUpper(test_string);
cout << "测试后数据:" + test_string << endl;
return 0;
}
实验结果图:
实验结论笔记:
? ? ? ? 在不改变传入数据的情况下,尽量使用常量引用。其原因有①把函数不会改变的形参定义为普通引用容易产生误解,即函数可以修改它的实参的值;②使用普通引用而非常量引用会极大地限制函数所能接受的实参类型,如图六、图七所示。
实验代码:
# include<iostream>
# include<string>
# include<vector>
using namespace std;
// 同类型多值返回
// 返回A大写字母开头的单词
vector<string> F1_Msame(const vector<string> wordList)
{
vector<string> A_List;
for (const string word : wordList)
if (*(word.begin()) == 'A')
A_List.push_back(word);
return A_List;
}
int main()
{
// 测试数据
vector<string> test_data = {"bar","A","crush","brush","Apple","view","Aka"};
vector<string> result = F1_Msame(test_data);
for (auto w : result)
cout << w << " ";
return 0;
}
# include<iostream>
# include<string>
# include<vector>
# include<tuple>
using namespace std;
// 不同类型多值返回
// 返回每一个A大写字母开头的单词与它所对应的索引值
vector<tuple<string, size_t>> F2_Mdiff(const vector<string> wordList)
{
vector<tuple<string, size_t>> Aidx_List;
for (size_t i = 0; i != wordList.size(); ++i)
if (wordList[i][0] == 'A')
Aidx_List.push_back(make_tuple(wordList[i],i));
return Aidx_List;
}
int main()
{
// 测试数据
vector<string> test_data = {"bar","A","crush","brush","Apple","view","Aka"};
vector<tuple<string, size_t>> result = F2_Mdiff(test_data);
for (tuple<string, size_t> w : result)
cout << "(" << get<0>(w) << ','<< get<1>(w) << ')' <<endl;
return 0;
}
实验结果图:
实验笔记:
Ⅰ 双引号“”包含的字面值为字符串char*,单引号‘’包含的字面值为字符char;
Ⅱ C++11新标准规定,函数可以返回花括号{}包围的值的列表。此处返回的列表用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值由函数的返回类型决定。
实验代码:
# include<iostream>
# include<vector>
using namespace std;
// 函数一:对两个int类型进行加法运算
int intAdd(int a,int b)
{
int c = a + b;
return c;
}
// 函数二:对两个int类型进行减法运算
int intSub(int a, int b)
{
int c = a - b;
return c;
}
// 函数三:对两个int类型进行乘法运算
int intPro(int a, int b)
{
int c = a * b;
return c;
}
// 函数四:对两个int类型进行除法运算
int intDiv(int a, int b)
{
int c = 0;
if (b == 0)
throw runtime_error("除数不能为零!");
else
c = a / b;
return c;
}
int main()
{
vector<int (*)(int, int)> FuncCalList = {intAdd,intSub,intPro,intDiv};
for (auto Fptr : FuncCalList)
cout << Fptr(2, 4) << endl;
return 0;
}
实验结果图:
实验笔记:
Ⅰ 函数名作为一个值使用时,该函数自动地转换为指针;
Ⅱ 可以直接使用指向函数的指针调用该函数,无须提前解引用指针;
[1] C++ Primer中文版:第5版 /(美)李普曼(Lippman,S.B.),(美)拉乔伊(Lajoie,J.),(美)默(Moo,B.E.)著;王刚,杨巨峰译. —北京:电子工业出版社,2013.9.(第四章-表达式,第六章-函数)
[2] C++语言程序设计 / 郑莉,董渊编著.—5版.—北京:清华大学出版社,2020.11.(第三章-函数)