《C++新经典》第13章 C++基本语言
13.1 语言特性、工程构成与可移植性
13.1.1 语言特性:过程+对象
- 面向过程
c语言是面向过程的程序设计:编写代码从上到下,逐步求精,功能封装成函数,需要结构体时定义,按顺序步步解决。C++完全支持。 - 基于对象和面向对象
基于对象的程序设计:将功能包装到类中,定义类对象并通过类对象调用成员函数实现各种功能。
面向对象的程序设计:基于对象的程序设计中额外运用继承性和动态性技术。
面向对象的优点:
(1)易维护。不同类只修改自己的接口就行。
(2)易扩展。继承和多态,少写代码。
(3)模块化。设置访问级别限制。 - 其他特性
13.1.2 C++程序和项目文件
- 项目(工程)中包含多个.cpp(源文件)和.h(头文件,放入函数声明,类、结构定义,#define等公共定义)文件。
- .hpp一般把定义和实现包含在一个文件里,能有效减少编译次数。
- cstdio等去掉扩展名
13.1.3 编译解释语言与可移植性问题
- 编译性语言,程序执行前有专门的编译过程,将程序编译成二进制可执行文件,执行时直接使用就行。
- 解释性语言,直接文本方式存储程序代码,不进行预先编译,执行程序时先解释后执行。
- 可移植性针对源代码,不同平台能编译运行。
13.2 命名空间和基本输入输出
13.2.1 命名空间
命名空间是为了防止名字冲突而引入的一种机制。可定义多个不同名的命名空间,命名空间可看成作用域,各命名空间内函数即便同名也互不影响。
- 命名空间定义
namespace 命名空间名 {
void radius() {}
} //无需分号
-
命名空间可不连续,可写在不同位置,甚至不同源文件中。未定义命名空间则定义,已定义命名空间则打开并添加内容。
-
外界访问命名空间
命名空间名::实体名(); //作用域运算符
- 简化书写(同名函数不要简化,通过命名空间区分)
using namespace 命名空间名;
实体名();
13.2.2 基本输入输出
- 基本输出
流就是一个字符序列。
#include <iostream> //输入输出流库
//using namespace std; //简化书写
int main() {
std::cout <<"over\n"; //cout,与iostream相关的标准输出对象,可当成屏幕
//>>输出运算符,类成员函数重载多个版本,处理不同数据类型
std::cout <<"end" <<std::endl;
//std::endl函数模板名,相当于函数指针,用于换行和刷新输出缓冲区(flush)。
return 0;
}
- 基本输入
int value;
//c中scanf需要地址符号&
//cin不需要地址符号&,因为引用
std::cin >>value; //cint,与iostream相关的标准输入对象,可当成键盘
//>>输入运算符,类成员函数重载多个版本,处理不同数据类型
13.3 auto、头文件防卫、引用与变量
13.3.1 局部变量和初始化
c++需要时随时定义变量,c只在函数头部定义变量。
for(int i{0}; i<100; i++) {} //循环内定义变量i只在循环内有效
定义变量初始化
int a=5;
int a(5);
int a{5};
int a = {5};
int b[]{1, 2, 3};
int b[] = {1, 2, 3};
int a = 3.5f; //可以编译通过
int a{3.5f}; //无法编译通过,数据不会截断,保证代码健壮性
13.3.2 auto关键字
变量的自动类型推断,声明变量时根据变量初始值的类型自动选择匹配类型,发生在编译期。系统能推动时无需显示指定类型,能避免书写很长的类型名。
auto b = true;
auto c = 'a';
auto d = 1.2;
auto i = 5;
13.3.3 头文件防卫式声明
head.h
#ifndef __HEAD__
#define __HEAD__
//......
#endif
13.3.4 引用
引用是未变量起别名,与原变量名占用同一块内存,并不额外占用内存,必须定义时初始化。
int value = 10;
int &refval = value;
refval = 13;
int a = 3;
int &b = a; //引用定义
int *p = &b; //取变量b地址
引用作为函数形参
void changeValue(int &v) {
v = 22;
}
13.3.5 常量
常量就是不变的量。
- const关键字
const int var = 17; //承诺值不变,实际可改变
int &var2 = (int &)var; //const int需类型转换
var2 = 5; //var2修改值后会重新开辟内存空间?
cout <<var <<endl; //17
cout <<var2 <<endl; //5,引用不是别名这么简单
int func() {return 3;}
int v1 = 12;
const int v2 = v1 + func(12); //运行时求值也行
- constexpr关键字
编译时求值,能够提升运行时的效率,有利于系统优化等。
constexpr int func(int abc) { //constexpr函数中代码要尽可能简单,变量定义时要初始化
int a3 = 5;
//int var; //error
//printf("good\n");//error,constexpr函数中需要调用constexpr函数?
return abc * a3;
}
constexpr int var1 = 1;
int value = 12;
constexpr int var2 = 11 + func(12); //func需定义成constexpr
constexpr int var2 = 11 + func(value); //错误,func中不能用变量value做参数
constexpr int var = 3;
int &var2 = (int &)var;
var2 = 5;
cout <<var <<endl; //5
cout <<var2 <<endl; //3
if(var == var2) {} //不成立,vs编译器内部特殊处理,实际var和var2不同内存
13.4 范围for、动态内存分配与nullptr
13.4.1 范围for语句
范围for语句,遍历序列,支持begin和end成员函数返回迭代器的容器就可以支持范围for语句。
int v[]{12, 13, 14, 16, 48};
for(auto x : v) //依次复制v数组中元素值给x
cout <<x <<endl;
for(auto x : {12, 13, 14, 16, 48}) //依次复制元素序列中值给x
cout <<x <<endl;
for(auto &x : v) //引用避免复制
cout <<x <<endl;
13.4.2 动态内存分配
内存存储空间分为程序代码区、静态存储区、动态存储区。
内存细分区域:
- 栈,函数局部变量,编译器自动分配释放。栈空间有限,系统规定,分配快。
- 堆,程序员malloc/new申请,及时free/delete释放(节省系统资源,防止耗尽导致程序崩溃,忘记释放程序结束后操作系统回收)。堆空间由程序员自由分配,大小理论上不超过实际物理内存,分配慢。
- 全局/静态存储区,放置全局变量和静态变量,程序结束时释放。
- 常量存储区,存放常量(“you”字符串常量等),不允许修改。
- 程序代码区。
- malloc和free
系统函数,堆空间中分配和释放内存。memory allocation动态内存分配。
void * malloc(int NumBytes); //失败NULL
void free(void *Ptr);
int *p = (int *)malloc(sizeof(int)*10);
if(p != NULL) {
int *q = p;
*q++ = 1;
*q++ = 5;
free(p);
p = NULL;
}
- new和delete
运算符,不是函数。比malloc和free做了额外的初始化和清理工作。
指针变量名 = new 类型标识符; //未初始化
指针变量名 = new 类型标识符(); //默认值初始化
指针变量名 = new 类型标识符(初始值); //指定值初始化
指针变量名 = new 类型标识符[内存单元个数]; //数组,delete[]释放
int *p = new int[100];
if(p != NULL) {
int *q = p;
*q++ = 1;
*q++ = 5;
delete[] p;//delete[]回收整个数组,[]内写了数字无意义,会被忽略
//delete p只会回收第一个数组元素空间
p = NULL;
}
13.4.3 nullptr
空指针,与指针相关尽量使用nullptr,不使用NULL。
nullptr能够避免在整数和指针间发生混淆。
cout <<type(NULL).name() <<endl; //int
cout <<type(nullptr).name() <<endl; //std::nullptr_t
void func(void *p) {
printf("func(void *p)\n");
}
void func(int p) {
printf("func(int p)\n");
}
int main() {
func(NULL); //int
func(nullptr); //void*
return 0;
}
13.5 结构、权限修饰符与类
13.5.1 结构
C++中结构功能扩展了,可以在其中定义成员函数(方法),默认public的class。
struct student {
int number;
char name[10];
void func() {
number++;
}
}
student stu; //struct可省略
stu.number = 1001;
stu.func();
13.5.2 public和private权限修饰符
- public,共有/公共。修饰的成员(成员变量、成员函数),能被外界访问,类似外部接口。
- private,私有,修饰的成员(成员变量、成员函数),只能被内部定义的成员函数使用。
struct(继承也)默认public,class(继承也)默认private。
13.5.3 类简介
用户自定义是数据类型(关联了操作),定义的类变量称为对象,一块能存储数据并具有某种类型的内存空间。
13.5.4 类的组织
类定义放在h头文件,类实现放在cpp源文件,使用时导入h头文件。
13.6 函数新特性、inline内联函数与const详解
13.6.1 函数后置返回类型
函数形参不使用时可以无形参名,函数调用是必须传递实参值。
int func(int a, int) { //前置返回类型
return 4;
}
func(1, 2);
后置返回类型,函数声明或定义中将返回类型写在参数列表之后,,用于复杂返回类型和特殊场合。
auto func(int, int) -> int;
auto func(int a, int) b -> int {
return 1;
}
13.6.2 inline内联函数
inline int func(int a) { //函数增加inline,内联函数
return a*a+3;
}
函数体很小,调用又很频繁的函数要频繁地进行压栈、出栈动作以处理函数调用和返回的问题,要频繁地开辟内存,耗费系统性能。
增加inline效果:
- 影响编译器,编译阶段,系统尝试调用函数替换为函数本体(不再进行函数调用),提升性能。
- 程序员对编译器的建议,做与不做的决定权在编译器。
- inline内联函数的定义就放在头文件中。
内联函数用函数本体取代函数调用,增加效率?,代码膨胀(函数体要尽可能短小),代码在程序运行时占用代码段内存。
注意点:
- 编译器影响内联结果,有优化成结果,表达式,函数体替换等。
- inline函数尽量简单,代码少,无复杂循环、分支、递归等,否则编译器可能拒绝函数内联。
- constepxr函数用于常量表达式,简单无多余语句,可看作更严格的内联函数,constexpr自带inline属性。
- 内联函数像宏展开(#define),有类型检查等区别。
13.6 函数特殊写法
- void表示无返回类型或无输入参数
void func1(void) {
printf("over\n");
//return; //return void;
}
void func2(void) {
return func1(void);
}
- 函数返回指针和返回引用
int *myfunc1() {
int tmp = 9; //tmp应该成为全局变量
return &tmp;//局部变量有隐患
}
int &myfunc2() {
int tmp = 9;//tmp应该成为全局变量
cout <<&tmp <<endl;
return tmp; //局部变量有隐患
}
int &k = myfunc2();
cout <<&k<<endl;
int k = myfunc2();
cout <<&k<<endl;
- 无参函数定义
int myfunc3(void) {reurn 1;}
int myfunc4() {reurn 1;}
- 函数不调用,可以只声明,无定义。
- 函数可以多次声明,只能一次定义。
- 通过指针或引用,函数可以改变外界实参的值。
- 函数同名,形参个数或类型不同,叫函数重载。
//函数重载不成立
void fs(const int i){}
void fs(int i){}
13.6.4 const char *、char const *与char * const的区别
- const char *p与char const *p等价
常量指针,指向内容固定(不能通过指针修改变量值),指向变量不固定(指针能重新赋值)
char str1[] = "you";
char str2[] = "me";
const char *p; //char const *p;
p = str1;
p[0] = 'Y'; //错误
p = str2; //正确
- char * const p
指针常量,指向变量固定(指针不能重新赋值),指向内容不固定(能通过指针修改变量值)
char str[] = "you";
char * const p = str;
*p = "Y"; //正确
p++; //错误
-
const char * const p或char const * const p
指向变量和指向内容都固定 -
const引用
int i = 100;
const int &a = i; //不能通过引用变量修改值
i = 200; //正确
a = 200; //错误
const int &b = 156;//正确,字面值初始化常量引用
int &b = 156;//错误
b = 200; //错误,b看作常量,不能修改值
13.6.5 函数形参中的const引用
sstruct student {int num;};
void fs(const student &stu) { //可接受const引用,普通引用,字面值常量
//stu.num = 1010;//const引用不能修改
}
student abc;
const student &def = abc;
fs(def); //若fs形参无const,则错误
void func(const int &a) {}
func(156); //正确
13.7 string类型
13.7.1 总述
string处理可变长字符串,vector是集合、容器或者动态数组。
13.7.2 string类型简介
标准库中类型,代表可变长字符串。
#include <string>
using namesapce std;
13.7.3 定义和初始化string对象
string s1;//默认""空串
string s2 = "you";//复制字符串内容
string s3("you");//同上
string s4 = s2; //复制s2中内容
int num = 6;
string s5(num, 'a'); //num个'a'组成字符串,系统内部会创建临时对象
13.7.4 string对象常用操作
- empty(),是否为空
- size()或length(),字节/字符数,返回类型unsigned
string s = "我爱中国";
cout <<s.size() <<endl; //8,1个汉字2个字节
cout <<s.length() <<endl;//8,1个汉字2个字节
- s[n],第n个字符,0开始,小于size(),访问“”空string会出错
- s1+s2,字符串拼接得到新string对象
- s1=s2,string内容复制
- s1==s2,长度相同,字符也全相同,大小写敏感
- s1!=s2,不相等
- s.c_str(),返回const char *常量指针,'\0’结尾
string s = "you";
const char * p = s.c_str();
char str[100];
strcpy_s(str, sizeof(str), p);
cout <<str <<endl;
- 读写string对象
string s;
cin >>s; //回车或空格符结束一次读取,且连续多个回车空格符会被读取后扔掉
cout <<s <<endl;
- 字面值和string相加
string s1 = "abc";
string s2 = "defg";
string s3 = s1 + " and " + s2 + 'h';
//因素类型转换
//可认为s1+" and ",生成临时string对象
- string中范围for的使用
string s = "you";
for(auto c: s) { //遍历字符序列
cout <<c <<endl;
}
for(auto &c: s) { //修改字符
c = toupper(c);
}
cout <<s <<endl;
13.8 vector类型
13.8.1 vector类型简介
容器,集合或动态数组,可以放入若干相同类型对象。
#include <vector>
using namesapce std;
vector<int> v;
vector不能装引用。引用只是别名,不是对象。
vector<int &> v; //错误
13.8.2 定义和初始化vector对象
- 空vector
vector<string> str;
vector.push_back("one");
vector.push_back("two");
- vector对象元素复制
vector<string> str2(str);
vector<string> str3 = str;
- 初始化列表赋初值
vector<string> def = {"aaa", "bbb", "ccc"};
vector<string> def2 = {};
- 创建指定数量的元素
vector<string> str2(5, "hello");
- 多种初始化
vector i1(10); //10个0
vector i2{10}; //1个10
vector i3(10, 1); //10个1
vector i3{10, 1}; //1个10,1个1
vector<string> s1{"aaa"};
vector<string> s2{10}; //10个""
vector<string> s3{10, "aaa"}; //10个"aaa"
13.8.3 vector对象操作
- empty是否为空。
vector<int> ivec;
if(ivec.empty())
cout <<"ivec is empty" <<endl;
- push_back,末尾增加元素
vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
for(int i=3; i<=100; i++)
ivec.push_back(i);
- size,元素个数
cout <<ivec.size() <<endl;
- clear,移除所有元素,容器清空
ivec.clear(); //容器元素为指针,并不释放指针指向的内存
cout <<ivec.size() <<endl;
- v[n],v中第n个元素,0开始,小于size(),超过范围或访问空vector都会产生不可预测结果。
ivec.clear();
ivec.push_back(1);
ivec.push_back(2);
cout <<ivec[1] <<endl;
- 赋值运算符=
vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
for(int i=3; i<=100; i++)
ivec.push_back(i);
vector<int> ivec2;
ivec2.push_back(111);
ivec2 = ivec;
ivec2 = {12, 13, 14, 15};
cout <<ivec2.size() <<endl;
- 相等==和不等!=
两个vector对象相等:元素数量相同,对应位置元素值相同,否则不等。
vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
for(int i=3; i<=100; i++)
ivec.push_back(i);
vector<int> ivec2;
ivec2 = ivec;
if(ivec2 == ivec)
cout <<"ivec2 == ivec" <<endl;
ivec2.push_back(i11);
if(ivec2 != ivec)
cout <<"ivec2 != ivec" <<endl;
- 范围for应用
vector<int> vecvalue{ 1, 2, 3, 4, 5 };
for(auto &vecitem : vecvalue)
vecitem *= 2;
for(auto vecitem : vecvalue)
cout <<vecitem <<endl;
for语句中,不要改变vector的容量,增加、删除元素都不可以。
vector<int> vecvalue{ 1, 2, 3, 4, 5 };
for(auto vecitem : vecvalue) {
vecvalue.push_back(888);//错误
cout <<vecitem <<endl;
}
13.9 迭代器
13.9.1 迭代器简介
迭代器是一种遍历容器内元素的数据类型,像指针,可以理解为迭代器用来指向容器中的某个元素。
string和vector可以通过[](下标)访问元素,但建议通过迭代器访问。
13.9.2 容器的迭代器类型
vector<int> iv = {1,2,3};
vector<int>::iterator iter;
//iterator是每个容器里面定义的成员(类型名)
//将vector<int>::iterator理解为一种类型,专门用于迭代器,该类型定义的变量就是迭代器
13.9.3 迭代器[r]begin/[r]end操作
- 迭代器
每种容器都定义了begin和end的成员函数,返回迭代器类型。
begin返回迭代器指向容器第一个元素,end返回迭代器指向末端元素后面不存在的元素(标志,岗哨作用)。
容器为空,则begin和end返回的迭代器相同。
vector<int> iv = {1,2,3};
for(vector<int>::iterator iter = iv.begin(); iter != iv.end(); iter++)
cout <<*iter <<endl;
- 反向迭代器
rbegin返回迭代器指向容器最后一个元素,end返回迭代器指向首端元素前面不存在的元素。
vector<int> iv = {1,2,3};
for(vector<int>::iterator riter = iv.rbegin(); iter != iv.rend(); iter++)
cout <<*riter <<endl;
13.9.4 迭代器运算符
- *iter,返回迭代器指向元素的引用。
- ++iter,iter++让迭代器指向容器中的下一个元素。
- –iter,iter–让迭代器指向容器中的上一个元素。
vector<int> iv = {1,2,3};
for(vector<int>::iterator iter = iv.begin(); iter != iv.end(); iter++)
cout <<*iter <<endl;
//++iter;//错误
--iter;
cout <<*iter <<endl;
- iter1 == iter2或iter1 != iter2,判断两个迭代器是否相同。若两个迭代器指向同一个元素则相等。
- 结构成员的引用。
struct student {
int num;
};
vector<student> sv;
student su;
su.num = 100;
sv.push_back(su); //复制结构体对象到容器中,容器内保存结构体和复制的结构体是两个独立内存,相互无影响。
su.num = 200;
vector<student>::iterator iter = sv.begin();
cout <<(*iter).num <<endl;
cout <<iter->num <<endl;
迭代器间相减表示两个迭代器间距离,迭代器加减数字表示跳过多少元素,不常用。
13.9.5 const_iterator迭代器
迭代器指向的元素的值不能改变,迭代器本身能改变,类似常量指针,只能从容器中读元素。
vector<int> iv = {1,2,3};
for(vector<int>::const_iterator iter = iv.begin(); iter != iv.end(); ++iter) {
//*iter = 4;//错误
cout <<*iter <<endl;
}
for(auto iter = iv.cbegin(); iter != iv.cend(); ++iter) {
cout <<*iter <<endl;
}
//容器对象是常量,必须使用const_iterator
const vector<int> iv2 = {1,2,3};
for(vector<int>::const_iterator iter = iv2.begin(); iter != iv2.end(); ++iter)
cout <<*iter <<endl;
13.9.6 迭代器失效
vector<int> iv = {1,2,3};
for(auto v : iv) {
cout <<v <<endl;
}
//等价于
for(auto iter = iv.cbegin(); iter != iv.cend(); ++iter) {
cout <<*iter <<endl;
}
任何改变容器容量的操作,如push_back,都会使当前容器对象迭代器失效。
操作迭代器过程中不要改变容器容量,即增删容器中的元素。
若插入或删除元素,立即break。
for(auto iter = iv.begin(); iter != iv.end(); ++iter) {
iv.push_back(888);
break;
}
//重新遍历
for(auto iter = iv.begin(); iter != iv.end(); ++iter) {
//
}
不断插入多条数据
int iCount 0;
auto iter = iv.begin();
while(iter != iv.end()) {
iter = iv.insert(iter, iCount+80);
iCount++;
if(iCount > 10)
break;
++iter;
}
释放容器
iv.clear();
auto iter = iv.begin();
while(iter != iv.end())
iter = iv.erase(iter); //erase返回下一个元素位置
while(!iv.empty()) {
auto iter = iv.cbegin();
iv.erase(iter);
}
13.9.7 范例演示
string str("you");
for(auto iter = str.begin(); iter != str.end(); ++iter)
*iter = toupper(*iter);
cout <<str <<endl;
struct conf {
char itemname[40];
char iemcontent[100];
};
char *getinfo(vector<conf *> &conflist, const char *pitem) {
for(auto pos = conflist.begin(); pos != conflist.end(); ++pos)
if(_stricmp((*pos)->itemname, pitem) == 0)
return (*pos)->itemcontent;
return nullptr;
}
int main() {
conf *p_conf1 = new conf;
strcpy_s(p_conf1->itemname, sizeof(p_conf1->itemname), "ServerName");
strcpy_s(p_conf1->itemcontent, sizeof(p_conf1->itemcontent), "1");
conf *p_conf2 = new conf;
strcpy_s(p_conf2->itemname, sizeof(p_conf2->itemname), "ServerID");
strcpy_s(p_conf2->itemcontent, sizeof(p_conf2->itemcontent), "1000");
vector<conf *> conflist;
conflist.push_back(p_conf1);
conflist.push_back(p_conf2);
char *p_tmp = getinfo(conflist, "ServerName");
if(p_tmp != nullptr)
cout <<p_tmp <<endl;
//释放内存
for(auto pos = conflist.begin(); pos != conflist.end(); ++pos)
delete (*pos);
//释放内存
//for(auto &item : confilst)
// delete item;
//conflist.clear();//可不需要,容器本身内容,容器失效后系统会自动释放
return 0;
}
13.10 类型转换
13.10.1 隐式类型转换
系统自动进行,无需人为介入的类型转换。
int m = 3+45.6
13.10.2 显式类型转换(强制类型转换)
int k = 5 % (int)3.2;
k = 5 % int(3.2);//函数风格
4种强制类型转换(强制类型转换运算符或操作符)
强制类型转换名<type>(express);
强制类型转换名是static_cast、dynamic_cast、const_cast、reinterpret_cast之一;
type是转换的目标类型;
express是要转换的值。
- static_cast
静态转换,正常转换,编译时进行类型转换检查,与C语言中强制类型转换差不多,可显示替换编译器执行的隐式类型转换。
相关类型转换。
double f = 100.34f;
int i1 = (int)f; //c风格
int i2 = static_cast<int>(f); //c++风格
子类转父类。
class A {};
class B : public A {};
int main() {
B b;
A a = static_cast<A>(b); //子类转父类
return 0;
}
void*与其他类型指针间转换。
int i = 10;
int *p = &i;
void *q = static_cast<void *>(p);
int *db = static_cast<int *>(q);
不能用于指针类型间转换。
double f = 100.0;
double *pf = &f;
int *i = static_cast<int *>(pf); //编译无法通过
float *fd = static_cast<float *>(pf);//编译无法通过
-
dynamic_cast
运行时类型识别和检查,主要用于父类型转成子类型,检查代价昂贵,但保证了转换的安全性。 -
const_cast
只能用于去掉指针或引用的const属性,编译时类型转换检查。
const int ai = 90;
int *pai = (int *)&ai;
*pai = 120; //未定义行为
cout <<ai <<endl;
cout <<*pai <<endl;
const int ai = 90;
//int ai2 = const_cast<int>(ai); //错误,const_cast用于指针或引用
const int *pai = &ai;
int *pai2 = const_cast<int *>(pai);
*pai2 = 120; //未定义行为
cout <<ai <<endl;
cout <<*pai <<endl;
int i = 10;
double d = const_cast<double>(i); //错误,const_cast不能改变表达式类型,需使用static_cast
- reinterpret_cast
编译时类型转换检查,表示重新解释,重新解读(所操作内容解释未另一种不同类型),用来处理无关类型的转换,任意转。
整型(地址)与指针间转换。
long long lv1 = 8898899400;
int *piv2 = reinterpret_cast<int *>(iv1); //整型(地址)转指针
long long ne = reinterpret_cast<long long>(piv2); //指针类型转整型
一种类型指针转换未另外一种类型指针,按照转换后的类型重新解释内存中的内容。
int i = 10;
int *pi = &i;
int *p2 = reinterpret_cast<int *>(&i);
char *pc = reinterpret_cast<char *>(pi);
void *pvoid = reinterpret_cast<void *>(pi);
int *pi2 = reinterpret_cast<int *>(pvoid );
安全性差,危险。
int iv1 = 100;
long long lv1 = 8898899400;
int *piv1 = (int *)iv1; //c风格
int *piv2 = reinterpret_cast<int *>(iv1); //c++风格
piv2 = reinterpret_cast<int *>(lv1);
long long ne = reinterpret_cast<long long>(piv2); //指针类型转整型