typedef、using 类型别名
-
传统类型别名使用关键字typedef:typedef sometype alias;
新标准使用类型别名声明using:using alias = sometype;
-
如果某个类型别名指代的是复合类型或常量,那么把它用到声明语句里就会产生意想不到的后果;如果直接使用别名进行替换,理解可能会发生偏差;见test_1
auto
- C++11标准引入了auto类型说明符,让编译器去分析表达式所属的类型;auto通过初始值来推算变量的类型,那么显然auto定义的变量必须有初始值
- 使用auto可以在一条语句中声明多个变量,因为一条声明语句只能够有一个基本数据类型,所以该语句中所有变量的初始基本类型都必须一样
- 编译器推断出的auto类型有时和初始类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则
- 使用引用实际上使用的是引用的对象;特别是引用当作初始值时,真正参与初始化的其实是引用对象的值,此时编译器以引用对象的类型作为auto的类型;见test_2
- auto一般会忽略掉顶层const,同时底层const则会保留;如果希望推断出的auto类型是一个顶层const,则需要明确指出;当设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留;见test_2
decltype
- 希望从表达式的类型推断出要定义的变量类型,但不想用该表达式的值初始化变量;C++11标准引入decltype,通过编译器分析表达式并得到其类型,却不计算表达式的值
- 在decltype与引用一起使用时,引用表示本来的意义而不是指代其对象,这点与auto第4点处不同;例如:如果reference是一个引用,那么decltype(reference)的结果就是引用类型,因此decltype(reference) reference_decltype变量就必须要初始化;见test_3
- decltype和auto另一处重要的区别是,decltype的结果类型与表达式的形式密切相关:对于decltype表达式,如果变量名加上了一对括号,得到的类型与不加括号时不同。如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器会把它当作一个表达式。变量是一个种可以作为赋值语句左值的特殊表达式,因此decltype就会得到引用类型;见test_3
- 切记: decltype((variable))的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用
static_cast、const_cast、reinterpret_cast、dynamic_cast 类型转换
-
如果两种类型可以相互转换,那么也可以称这两种类型是相关联的
-
算术类型之间的隐式转换的设计思路是:为了计算过程中尽量避免损失精度,其中运算符的运算对象将转换为最宽类型
-
旧式显式转换包含:
- type (expr) // 函数形式的强制类型转换
- (type) expr // C语言风格的强制类型转换
根据所涉及的类型不同,旧式强制类型转换分别具有与const_cast、static_cast或reinterpret_cast相似的行为
-
新式显示强制转换:static_cast、const_cast、reinterpret_cast、dynamic_cast
- static_cast:
- 任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast;
- 对于需要将从大向小类型转换的情况下,static_cast意味着编码人员告诉编译器,自己明确知道并不在乎潜在的精度损失,因此编译器警告信息将会关闭
- static_cast对于编译器无法自动执行的类型转换也可以使用;例如可以找回void*指针的原始类型,但是必须保证转换后的类型就是原指针的类型,否则使用转换后的指针将产生未定义后果
- const_cast:
- const_cast只能改变运算对象的底层const属性;将常量对象转换为非常量对象,即去掉const性质;该转换旨在获得对象的写操作权限,此时要注意如果原对象本身就不是常量,那么const_cast获得写权限是合法的行为,如果对象是常量,则用const_cast执行写操作就会产生未定义的后果
- 只有const_cast能改变表达式的常量属性;使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误
- 不能用const_cast改变表达式的类型(不能干static_cast的事情)
- reinterpret_cast:
- reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释
- 使用reinterpret_cast极其危险,它本质上依赖于编译器,必须对涉及的类型和编译器实现转换的过程非常了解;例如通过reinterpret_cast将一个int *指针p转换为char *指针之后,后面编译器将完全忘记p原有的int *属性,将认定其值为char *,使用不当将会引发糟糕的后果
- dynamic_cast:
- RTTI run-time type identification 运行时类型识别功能由两个运算符实现:typeid 和 dynamic_cast
- dynamic_cast运算符用于将基类的指针或引用安全地转换成派生类的指针或引用
- static_cast:
-
强烈建议程序员避免使用强制类型转换,尤其是reinterpret_cast总是充满了风险;const_cast在有重载函数的上下文使用无可厚非,但是在其他情况意味着程序存在某种设计缺陷;原则上static_cast、dynamic_cast也不应该频繁使用;显示强制转换样例见test_4
/**
* @Author: phd
* @Date: 2019/12/14
* @Site: github.com/phdsky
* @Description: NULL
*/
#include <iostream>
using namespace std;
void test_1() {
typedef char * char_pointer_alias;
const char_pointer_alias const_char_pointer = 0; // this is a const pointer pointing to char object
const char * const_char_pointer_another = 0; // this is a pointer pointing to const char object
}
void test_2() {
int value_int = 0, &reference_int = value_int;
auto auto_int = reference_int;
cout << typeid(auto_int).name() << endl;
const int const_int = 0, &reference_const_int = const_int;
auto auto_int_another = const_int;
cout << typeid(auto_int_another).name() << endl; // this is a int
const auto const_auto_int = const_int;
cout << typeid(const_auto_int).name() << endl; // this is a const int
auto &auto_reference = const_int;
// auto &auto_reference_another = 233; // error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
const auto &auto_reference_other = 233;
}
void test_3() {
const int const_int = 0, &reference_const_int = const_int;
decltype(const_int) const_int_another = 233;
decltype(reference_const_int) reference_const_int_another = const_int_another;
// decltype(reference_const_int) reference_const_int_other; // error: declaration of reference variable 'reference_const_int_other' requires an initializer
int value_int = 666;
//decltype((value_int)) reference_int; // error: declaration of reference variable 'reference_int' requires an initializer
decltype(value_int) value_int_another;
}
void test_4() {
// static_cast
double double_value = 666.66;
int int_value = static_cast<int>(double_value) / 2;
cout << int_value << endl;
void *void_pointer = &double_value;
double *double_pointer = static_cast<double*>(void_pointer);
cout << *double_pointer << endl;
// const_cast
char char_value = 'p';
const char *const_char_pointer = &char_value;
// const_char_pointer = 'h'; // error: assigning to 'const char *' from incompatible type 'char'
char *char_pointer = const_cast<char*>(const_char_pointer);
*char_pointer = 'd';
cout << *const_char_pointer << ' ' << *char_pointer << endl;
// char *char_pointer_using_static_cast = static_cast<char*>(const_char_pointer); // error: static_cast from 'const char *' to 'char *' is not allowed
string static_cast_string = static_cast<string>(const_char_pointer);
// string const_cast_string = const_cast<string>(const_char_pointer); // error: const_cast to 'std::__1::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >'), which is not a reference, pointer-to-object, or pointer-to-data-member
// reinterpret_cast
int *int_pointer = &int_value;
char *char_pointer_another = reinterpret_cast<char *>(int_pointer);
string string_value(char_pointer_another);
cout << *int_pointer << ' ' << *char_pointer_another << ' ' << string_value << endl;
// dynamic_cast
}
int main(int argc, char *argv[]) {
test_1();
test_2();
test_3();
test_4();
return 0;
}