《C++新经典》第13章 C++基本语言

130 篇文章 4 订阅
15 篇文章 2 订阅

13.1 语言特性、工程构成与可移植性

13.1.1 语言特性:过程+对象

  1. 面向过程
    c语言是面向过程的程序设计:编写代码从上到下,逐步求精,功能封装成函数,需要结构体时定义,按顺序步步解决。C++完全支持。
  2. 基于对象和面向对象
    基于对象的程序设计:将功能包装到类中,定义类对象并通过类对象调用成员函数实现各种功能。
    面向对象的程序设计:基于对象的程序设计中额外运用继承性和动态性技术。
    面向对象的优点:
    (1)易维护。不同类只修改自己的接口就行。
    (2)易扩展。继承和多态,少写代码。
    (3)模块化。设置访问级别限制。
  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 基本输入输出

  1. 基本输出

流就是一个字符序列。

#include <iostream> //输入输出流库
//using namespace std; //简化书写

int main() {
	std::cout <<"over\n"; //cout,与iostream相关的标准输出对象,可当成屏幕
	//>>输出运算符,类成员函数重载多个版本,处理不同数据类型
	
	std::cout <<"end" <<std::endl; 
	//std::endl函数模板名,相当于函数指针,用于换行和刷新输出缓冲区(flush)。
	return 0;
}
  1. 基本输入
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 常量

常量就是不变的量。

  1. 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); //运行时求值也行

  1. 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”字符串常量等),不允许修改。
  • 程序代码区。
  1. 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;
}
  1. 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 函数特殊写法

  1. void表示无返回类型或无输入参数
void func1(void) {
	printf("over\n");
	//return; //return void;
}
void func2(void) {
	return func1(void);
}
  1. 函数返回指针和返回引用
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的区别

  1. 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; //正确
  1. char * const p
    指针常量,指向变量固定(指针不能重新赋值),指向内容不固定(能通过指针修改变量值)
char str[] = "you";
char * const p = str;
*p = "Y"; //正确
p++; //错误
  1. const char * const p或char const * const p
    指向变量和指向内容都固定

  2. 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对象常用操作

  1. empty(),是否为空
  2. size()或length(),字节/字符数,返回类型unsigned
string s = "我爱中国";
cout <<s.size() <<endl; //8,1个汉字2个字节
cout <<s.length() <<endl;//8,1个汉字2个字节
  1. s[n],第n个字符,0开始,小于size(),访问“”空string会出错
  2. s1+s2,字符串拼接得到新string对象
  3. s1=s2,string内容复制
  4. s1==s2,长度相同,字符也全相同,大小写敏感
  5. s1!=s2,不相等
  6. 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;
  1. 读写string对象
string s;
cin >>s; //回车或空格符结束一次读取,且连续多个回车空格符会被读取后扔掉
cout <<s <<endl;
  1. 字面值和string相加
string s1 = "abc";
string s2 = "defg";
string s3 = s1 + " and " + s2 + 'h';
//因素类型转换
//可认为s1+" and ",生成临时string对象
  1. 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对象

  1. 空vector
vector<string> str;

vector.push_back("one");
vector.push_back("two");
  1. vector对象元素复制
vector<string> str2(str);
vector<string> str3 = str;
  1. 初始化列表赋初值
vector<string> def = {"aaa", "bbb", "ccc"};
vector<string> def2 = {};
  1. 创建指定数量的元素
vector<string> str2(5, "hello");
  1. 多种初始化
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对象操作

  1. empty是否为空。
vector<int> ivec;
if(ivec.empty())
	cout <<"ivec is empty" <<endl;
  1. push_back,末尾增加元素
vector<int> ivec;
ivec.push_back(1);
ivec.push_back(2);
for(int i=3; i<=100; i++)
	ivec.push_back(i);
  1. size,元素个数
cout <<ivec.size() <<endl;
  1. clear,移除所有元素,容器清空
ivec.clear(); //容器元素为指针,并不释放指针指向的内存
cout <<ivec.size() <<endl;
  1. v[n],v中第n个元素,0开始,小于size(),超过范围或访问空vector都会产生不可预测结果。
ivec.clear();
ivec.push_back(1);
ivec.push_back(2);
cout <<ivec[1] <<endl;
  1. 赋值运算符=
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;
  1. 相等==和不等!=
    两个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;
  1. 范围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操作

  1. 迭代器
    每种容器都定义了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;
  1. 反向迭代器
    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 迭代器运算符

  1. *iter,返回迭代器指向元素的引用。
  2. ++iter,iter++让迭代器指向容器中的下一个元素。
  3. –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;
  1. iter1 == iter2或iter1 != iter2,判断两个迭代器是否相同。若两个迭代器指向同一个元素则相等。
  2. 结构成员的引用。
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是要转换的值。

  1. 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);//编译无法通过
  1. dynamic_cast
    运行时类型识别和检查,主要用于父类型转成子类型,检查代价昂贵,但保证了转换的安全性。

  2. 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
  1. 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); //指针类型转整型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值