面向过程
面向对象
基础知识
C++语言的发展
1979年 本贾尼.斯特劳斯特劳普C++之父,开发了C with class
带类的c语言
C语言是C++语言的子集 C++语言是C语言的扩展
1983年 C++正式命名
1985年 CFront1.0 第一个商用的C++编译器
1987年 GNU C++
1990年 Borland C++
1992年 Microsoft C++, IBM C++
1998年 ISO C++98
2003年 ISO C++03
2011年 ISO C++2011/C++11/C++0x 大量新增功能
2014/2017 C++增加很多新功能
c java python c++
版本的稳定性很重要
C++特点
1.运行速度比C语言慢,比java/python快得多
2.用C语言开发效率低下,C++开发效率很高
兼顾运行速度和开发效率
#C++语言的应用领域
1.游戏开发 强建模能力,性能高 cocos2d-x游戏引擎
网易 巨峰 电魂 边锋
2.科学计算 C++算法库
3.网络和分布式: ACE框架
4.桌面应用: VC/MFC VC++6.0 微软C++的编译器
MFC 图形界面开发工具包
QT C++的语法
5.操作系统和设备驱动 优化的C++编译器,使得C++执行效率可以和C相媲美
6.移动终端
7.军工 航天
C++比C语言更加丰富
1.C++支持面向对象编程, 宏观面向对象,微观面向过程
2.支持泛型编程(类型参数化)
3.支持异常机制
C语言的错误处理和正常逻辑是一样的代码
4.支持操作符重载
5.C++提供丰富的标准库 有丰富的第三方库
C++的使用
1.C++的编译器g++,如果使用gcc编译,需要带上-lstdc++
指定其使用标准C++的运行库
2.源文件扩展名: .cpp .cc .C .cxx .c++
最好使用.cpp
3.头文件 #include <iostream> iostream IO流
C++中标准库头文件一般都没有.h
当然也允许有.h
C++标准库中的内容放在一个叫做std名字空间中
using namespace std;
4.cout 对象 标准输出对象
cin 对象 标准输入对象
cerr 对象 标准错误输出对象
endl 对象 换行对象
<<: 输出运算符 cout cerr
>>: 输入运算符 cin
名字空间 命名空间
1.C++对程序中的标识符(类型、函数、变量)按照某种逻辑划分成若干个组
对标识符的逻辑划分
cin cout endl C++标准库的函数,对象,类型都位于std名字空间中
避免了名字冲突
2.定义名字空间的语法
namespace 名字空间的名字{
全局变量
类型定义
函数
}
3.使用名字空间
(1)作用域限定符 名字空间名::标识符
表示明确访问是特定名字空间中的特定标识符
虽然最麻烦,但是能准确访问
#include <iostream>
int main(){
std::cout << "Hello world!" << std::endl;
std::cout << 1919 << std::endl;
std::cout << 3.14 << std::endl;
std::cout << 2011 << "," << 1314 << "," << std::endl;
return 0;
}
没有加using namespace std 的使用方法
(2)名字空间指令
using namespace 名字空间名;
该语句可以在任何地方
表示在该指令之后,对指令所指的名字空间中所有标识符对当前作用域都可见
接下来都可以直接访问名字空间中的标识符,不需要加"名字空间名::"
当然也可以加
范围是的对当前作用域可见
(3)名字空间声明
using 名字空间::标识符
将指定名字空间中的某个标识符(成员)引入到当前作用域,可以直接访问
范围是导入到当前作用域
#include <iostream>
using namespace std;
//定义名字空间
namespace wd1{
int g = 1024;
void func(void){
cout << "func" << endl;
}
struct Stu{
int no;
char name[40];
};
}
int main(){
using wd1::g;
cout << g << endl;
cout << wd1::g << endl;
wd1::func();
struct wd1::Stu s = {};
using namespace wd1;
cout << g << endl;
func();
struct Stu s2 = {};
return 0;
}
4.匿名空间
编译器为没有指明名字空间的全局区的标识符置于匿名名字空间中
如果要指明访问匿名名字空间中的标识符 直接用 ::标识符
:: 作用域限定符
名字空间名::标识符 访问指定名字空间中的标识符
::标识符 访问匿名名字空间中的标识符 可以区分同名的全局变量和局部变量
```cpp
#include <iostream>
using namespace std;
//如果一个全局的标识符没有指定名字空间,编译器将会为其缺省地置于匿名名字空间中
int gv = 1024;
int main(){
cout << gv << endl;
cout << ::gv << endl; //访问指定名字(匿名名字)空间中的标识符
int gv = 9527;
cout << gv << endl;//访问局部的 局部优先原则 同名的局部变量会隐藏同名的全局变量
cout << ::gv << endl;//访问全局的
return 0;
}
5.名字空间合并
一个程序中,如果名字空间名字相同,则认为是同一个名字空间
里面的标识符会进行合并
#include <iostream>
using namespace std;
namespace myspace{
int x = 1024;
void func(){
cout << "func" << endl;
}
}
namespace myspace{
int y = 9527;
void bar(){
cout << "bar" << endl;
}
}
int main(){
using namespace myspace;
cout << x << endl;
cout << y << endl;
func();
bar();
return 0;
}
6.名字空间嵌套
一个名字空间包含另外一个名字空间
不能直接 using namespace 里层名字空间; 对于当前作用域而言是不可见
using namespace 外层名字空间;
using namespace 里层名字空间;
using namespace 外层名字空间::里层名字空间;
using namespace std;
int x=2;
namespace s1{
int x= 1024;
namespace s2{
int y =9527;
}
namespace s4{
int w = 520;
}
}
cout << s1::s2::y <<endl;
using s1::s2::y;
cout << y << endl;
using namespace s1;//对当前作用域可见
using s2::y;
! using namespace s3;//这样是错的,因为s2还未可见
using namespace s1::s2::s3;
#include <iostream>
using namespace std;
//int x = 1;
namespace s1{
int x = 1024;
namespace s2{
int y = 9527;
namespace s3{
int z = 1314;
}
}
namespace s4{
int x = 520;
}
}
int main(){
using s1::x;
cout << x << endl;
cout << s1::x << endl;
//cout << y << endl;
cout << s1::s2::y << endl;
using s1::s2::y;
cout << y << endl;
using namespace s1;
using s2::y;
//using namespace s3;
using namespace s1::s2::s3;
cout << z << endl;
using namespace s4;
cout << x << endl;//有两个名字空间中都有x 因为它们对当前作用域没有优先级关系 所以会有歧义
return 0;
}
注意:
如果用using namespace 名字空间;进行名字空间声明当前作用域可见
如果不同的两个作用域中含有相同名字和标识符,不访问没问题
但是如果用直接访问,则会产生歧义
#include <iostream>
using namespace std;
namespace s1{
int x = 1111;
}
namespace s2{
int x = 9527;
}
int main(){
cout << s1::x << endl;
cout << s2::x << endl;
using namespace s1;
cout << x << endl;
using namespace s2;
// cout << x << endl;//会有歧义
cout << s1::x << endl;
cout << s2::x << endl;
using s2::x; //导入到当前作用域
cout << x << endl;
cout << s1::x << endl;
//using s1::x;//当前作用域中有一个x了
return 0;
}
using namespace 名字空间; 进行声明不会有歧义
using 名字空间名::标识符 可能会有歧义
7.名字空间在多文件中进行编程
C++的结构体、联合和枚举
1.结构体
(1)C++中使用结构体时,直接可以省略struct关键字 Stu s ={} sizeof(s) == 1
(2)sizeof(空结构体),在C++中为1,C中为0
(3)C++结构体中可以定义函数,结构体中的函数可以直接访问成员
C++结构体中的函数,是用C结构体中可以定义函数指针的方式实现的
#include <iostream>
using namespace std;
//面向对象的编程思想 C++用C语言的实现 C语言中虽然没有函数,但是可以有函数指针
struct Date{
int year;
int mon;
int day;
//在结构体内部定义函数 可以直接访问结构体中的成员变量
void show(){
cout << year << "-" << mon << "-" << day << endl;
}
};
int main(){
Date d = {2021,8,24};
d.show();//C++相当于直接优化了函数指针,变成了一个函数
Date d1 = {2008,8,8};
d1.show();
cin >> d1.year >> d1.mon >> d1.day;
return 0;
}
2.联合
C++中支持匿名联合
借用联合的语法形式,描述变量在内存中的布局方式
匿名联合中的变量可以直接访问,不需要定义联合变量
#include <iostream>
using namespace std;
int main(){
//匿名联合 相当于定义了两个变量u1,c1
//匿名联合中的变量内存是共享的
union{
int u1;
char c1;
};
u1 = 0x12345678;
cout << hex << (int)c1 << endl;//输出78
int u2;
char c2;
return 0;
}
3.枚举
C++中的枚举是一种独立的类型,不能直接用整数值赋值给枚举变量
依然允许枚举变量或者是枚举值赋值给整数变量
#include <iostream>
using namespace std;
enum Dire{UP,DO,LE,RI};
int main(){
Dire d = UP;
d = DO;
//d = 0;//不能用整数给枚举变量赋值
cout << UP << endl;//UP的值等于0 d=0;不允许 C++加强了类型检查
int x = UP;
x = d;//枚举变量和枚举值可以赋值给整数
cout << x << endl;
return 0;
}
C++中的布尔类型
C中没有这种基础数据类型
C++提供了,不用头文件
bool类型占一个字节 sizeof(bool)=1;
true/false 1/0
非零即真 0 0.0 NULL '\0'
boolalpha 对于布尔类型直接显示 true/false 而不是1/0
noboolalpha 关闭
bool a = true;
cout << boolalpha << a << endl;//这样输出就是true 不是1
cout << boolalpha << true << endl;
a=123;a="Hello";a=3.14 都允许
可以把任何变量任何数据赋值非布尔变量 非零即真 直接输出的结果是0/1
#include <iostream>
using namespace std;
int main(){
bool a = false;
cout << a << endl;//0
a = true;
cout << a << endl;
cout << true << ":" << false << endl;
//boolalpha 对于布尔类型直接显示 true/false 而不是1/0
cout << boolalpha << a << endl;
cout << boolalpha << true << ":" << false << endl;
a = 123;
a = "Hello";
a = 3.14;
a = 2;
cout << noboolalpha << a << endl;//1
return 0;
}
C++中的运算符别名
正常情况下都不会使用
可以直接用后面的单词替换
&& and
|| or
! not
& bitand
^ xor
{ <%
} %>
[ <:
] :>
#include <iostream>
using namespace std;
int main(){
int year=2021;
//cin >> year;
if(year%4==0 and year%100!=0 or year%400==0){
cout << "闰年" << endl;
}
int arr<:5:> = <%5,4,3,1,2%>;
cout << arr<:3:> << endl;
return 0;
}
C++中的函数
1.支持重载(与返回值无关)
(函数名 参数) (打 电话)
吃饭 支付这种行为:可以用现金 支付宝
在同一个作用域下,函数名相同,参数列表不同,即构成重载
重载前提条件
在同一个作用域下面 平等关系(如果作用域不同就是重载,因为会屏蔽隐藏)
函数名相同
参数列表不同
1.参数类型不同(对应位置)
2.参数个数不同
3.常属性不同 (const) 指针和引用类型的常属性不一样
与函数返回类型无关
C++中,在调用重载的函数是,编译阶段根据函数调用时所传递的参数的个数和类型来决定调用重载的函数
静态运行时:在编译阶段确定调用哪个函数 静态联编 重载、模板
动态运行时:在运行阶段才确定调用哪个函数 动态联编 虚函数
C++中为什么可以支持重载?
链接 -S 生成汇编代码文件.s查看为何能区别
g++编译器会对程序中的函数名进行换名操作,将参数列表中的类型信息汇合到函数名中,以保证函数名的唯一
如何取消这个换名操作
extern "C" 要求g++编译器不要对函数进行C++换名操作,只对一个函数起作用,如果要全部,需用{}包含
以方便在C语言中调用C++编译生成的代码
#include <iostream>
using namespace std;
extern "C"
void func(){//没有
}
void bar(){//变了
}
extern "C"{
void call(){}//没有
void hello(){}//没有
}
int main(){
return 0;
}
重载时函数调用匹配问题
1.先进行精确匹配,参数个数和类型完全一样
2.有常属性的指针和引用可以和没有常属性的指针和引用的函数构成重载
如果非const版本和const版本都有,那么在调用时根据常属性进行匹配调用
如果只有一个版本的函数,没有常属性的数据 可以调用const版本,
但是const属性的一定不能调用非const版本的函数
可以自动加强const属性,但是不能贸然丢失const属性
#include <iostream>
using namespace std;
/*
void func(int *p){
cout << "func(int *)" << endl;
}
*/
void func(const int *p){
cout << "func(const int *)" << endl;
}
int main(){
int *p1;
const int *p2;
func(p1);//自动提升const
func(p2);
return 0;
}
3.如果在匹配过程中,没有完全的匹配,会对参数进行隐式转换
如果隐式转换的过程中,发现有两个或者以上的函数,都能够进行相等的转化,就会产生歧义
func(char,char)
func(int,int)
调用
func('a',78);//就会不知道调用哪个,因为转换量一样,都只要转一个实参
int max(int a,int b){
cout << "int,int" << endl;
return a>b>a:b;
}
int max(int a,int b,int c){
cout << "int,int,int" << endl;
if(a>=b && a>=c)
return a;
...
}
double max(double a,double b){
return a>b?a:b;
cout << "double,double" << endl;
}
int main(){
cout << max(1,2) << endl;
cout << max(1,2,3) << endl;
cout << max(3.14,3.33) << endl;
return 0;
}
------------------------------------------------
void func(){
cout << "func()" << endl;
}
//重定义
/*
void func(){
cout << "func()" << endl;
}
*/
//新旧定义歧义
/*
int func(){
return 1;
}
*/
void func(int a){
cout << "func(int)" << endl;
}
void func(double b){
cout << "func(double)" << endl;
}
void func(char a,char b){
cout << "func(char,char)" << endl;
}
但是如果写了这个,就会报错,因为从隐式转化的方面,会有选择
void func(int a,int b){
cout << "func(int a,int b)" << endl;
}
/*
void func(const int a){
cout << "func(const int)" << endl;
}
*/ //这样是错的,常属性重载有要求
struct Stu{
int a;
}
//引用&
void func(Stu& s){
cout << "func(Stu)" << endl;
}
void func(const Stu& s){
cout << "func(const Stu)" << endl;
}
void func(int *pi){}
void func(const int *pi){}
int main(){
func();
func('a');//没有char,自动提升为int
func(108);
func(97,'b');//即使没有也不会报错,有隐式转化
return 0;
}
2.支持默认值(缺省值)
long long int
在定义函数时,可以给函数参数以默认值(相当于赋值语句一样),那么在调用该函数时
(1)可以传递该位置的参数,也可以不传递(用缺省值)
(2)如果某个函数的一个参数有缺省值,那么该参数后面所有的参数都必须要有缺省值 void func(int x=1,int y)
缺省值靠右原则
(3)如果函数的声明和定义分开的情况下,那么缺省参数只能放在声明中 就是.h可以缺省,.cpp不能
(4))要注意和重载产生歧义 不运行不会错,运行会错
#include <iostream>
using namespace std;
//参数y有缺省值 该参数可以传 也可以不传 不传的采用的缺省值
long long int pow(int x,int y=2){
long long int res = 1;
int i;
for(i=1;i<=y;i++){
res *= x;
}
return res;
}
// 和缺省值产生了歧义
long long int pow(int x){
}
/*
void func(int x=1,int y){//如果允许 func(2)
}
*/
//声明
void func(int x,int y=1);
void bar(int x,int y=1,int z=2,int w=3){
cout << x << "," << y << "," << z << "," << w << endl;
}
int main(){
//cout << pow(10) << endl;//调用歧义
cout << pow(10,2) << endl;
cout << pow(10,3) << endl;
//bar();
bar(0);//0,1,2,3
bar(100,200);//100,200,2,3
bar(100,200,300);
return 0;
}
//void func(int x,int y=1){//分开的情况下,缺省值只能放声明中
void func(int x,int y){
}
3.支持哑元
函数的参数只有类型,没有名字的形参,谓之哑元
在重载++,--运算符时会使用哑元
注意重载产生重定义
#include <iostream>
using namespace std;
void func(void){
}
void func(int){//int
cout << "func(int)" << endl;
}
/*
void func(int x){
}
*/
int main(){
func(1);
func(2);
return 0;
}
4.支持内联
C++ inline声明的函数
(1)在调用内联函数时,用函数的二进制指令代码替换到函数调用指令,减少函数调用的时间开销
把这种策略称为内联
(2)inline关键字,只是建议编译器将指定的函数编译成内联,但是仅仅是建议而已
(3)频繁调用的简单函数适合内联,而稀少调用的复杂函数适合内联
(4)递归函数无法内联
5.其他
C语言有函数的隐式声明,C++没有 C++中调用的函数必须先声明
C++中如果声明函数的参数为空(啥也没写),就代表C语言中void的作用
C++中不能在调用无参函数时传递参数
C++动态内存
C动态内存: malloc/calloc/realloc/free sbrk/brk mmap/munmap
C++动态内存: new/delete 底层调用的是C语言的函数
new/delete 用来对单个变量内存的申请/释放
数据类型* 指针变量 = new 数据类型; //自动初始化为"零"
数据类型* 指针变量 = new 数据类型(初始值);
delete 指针变量;
new[]/delete[] 用来对数组进行内存申请/释放
#include <iostream>
using namespace std;
int main(){
int *p = new int;//new一个int类型的内存空间
cout << *p << endl;//这里会发现new出来的地址后面都一样
*p = 1024;
cout << p << endl;
cout << *p << endl;
delete p;//释放动态内存
double *pd = new double;
cout << *pd << endl;0 没赋值之前是0
*pd = 3.14159;
cout << pd << endl;
cout << *pd << endl;
delete pd;
p = new int(1024);//这种形式new完直接有值了
cout << p << endl;
cout << *p << endl;
delete p;
pd = new double(6.666);
cout << pd << endl;
cout << *pd << endl;
return 0;
}
new数组
#include <iostream>
using namespace std;
int main(){
int *p = new int[10]{111,222,1024};//申请10个int类型的内存地址
for(int i=0;i<10;i++){
cout << p[i] << " ";
}
cout << endl;
for(int i=0;i<10;i++){
p[i] = i;
}
for(int i=0;i<10;i++){
cout << p[i] << " ";
}
cout << endl;
delete[] p;
return 0;
}
new/delete和malloc/free的区别?
(1)new/delete 是C++操作符 malloc/free是C语言标准库的函数
(2)new在申请动态内存时不需要自己计算内存大小
malloc指定申请动态内存的字节数
(3)new会调用类的构造函数 malloc不会
new申请的动态内存不会有垃圾值
delete在释放动态内存时 会调用类的析构函数 但free不会
(4)malloc出错 返回NULL并且设置errno
new出错 抛出异常
(5)malloc返回值类型为 void*
new返回值类型 为 new的数据类型的指针
(6)new数组内存时需要用 new[]
malloc可以直接申请数组内存
引用
引用即别名
数据类型& 变量名 = 目标对象
注意:
引用变量必须直接初始化,形参列表引用没有直接初始化(在函数调用时初始化)
引用一旦初始化,不能更改引用对象(不能指向其他对象)
对引用对象的操作,直接影响其目标对象
#include <iostream>
using namespace std;
void swap(int *pa,int *pb){//指针的用法C中很熟悉
int tmp = *pa;//pa = &a;
*pa = *pb;
*pb = tmp;
}
//引用
void exchange(int& m,int& n){// m和n是实参a和b的别名 操作m和n 其实 就是操作实参
int tmp = m;
m = n;
n = tmp;
}
int main(){
int a = 1024, b = 9527;
cout << "a=" << a << ",b=" << b << endl;
exchange(a,b);
cout << "a=" << a << ",b=" << b << endl;
int m = 1, n = 2;
exchange(m,n);
cout << m << "," << n << endl;
return 0;
}
#include <iostream>
using namespace std;
int main(){
int a = 1024;
cout << &a << endl;//0x61fe0c
cout << a << endl;
int& r = a;//定义引用变量r 引用 a
cout << &r << endl;//0x61fe0c
cout << r << endl;
r = 9527;//对r操作就是对a操作
cout << r << endl;//9527
cout << a << endl;//9527
a = 1212;
cout << a << endl;
cout << r << endl;
double d = 3.14;
double& rd = d;
return 0;
}
引用变量必须初始化
int main{
! int &r;//不能通过编译
int a=10;
int& r1=a,r2;//可以 r1是int& r2是int
return 0;
}
引用不能直接常量
!int&r =10;//不能引用常量
const int&r =10;//这样就行
引用的应用
(1)函数的参数 在函数内存修改实参的值
(2)作为函数的返回值 需要确保在函数调用之后,返回的对象依然有效
能够返回全局变量 静态变量 动态内存对象的引用 引用参的引用(确保函数调用后依然存在就行)
不能返回局部变量的引用
double& getd(void){
double d;
return d;
}//这样可能不行
double& rd = getd();//不能返回局部变量的应用
能不能操作取决于运行 编译警告 引用的对象的值是不确定的,行为不确定,可能引发后续问题
(3)多态 父类引用类型引用子类对象
#include <iostream>
using namespace std;
void func(int& ra){
cout << ra << endl;
ra = 1111;
}
!
/*
double& getd(void){
double d = 6.666;
return d;//不能返回局部变量的引用
}//会报错这个
*/
int main(){
//int& r;
int a = 10;
int& r1 = a,r2;//r1是引用 int&, r2是int
func(a);
cout << a << endl;
//double& rd = getd();
//cout << rd << endl;
return 0;
}
引用与指针
(1)引用的本质其实就是指针,C++对指针的一个封装,很多场合引用和指针可以互换
(2)C++层面上 引用和指针有以下方面的不同
A.指针是实体变量,但是引用不是实体变量
sizeof(指针) == 4/8 当平台和编译器确定,大小唯一
int a=2; int& ra=a;sizeof(ra)==4
double d=1.0;double& rd=d;sizeof(rd)==8
引用的大小由引用的对象的类型决定
#include <iostream>
using namespace std;
int main(){
cout << sizeof(void*) << endl;//4/8
cout << sizeof(int*) << endl;//4/8
cout << sizeof(double*) << endl;//4/8
cout << "------------" << endl;
cout << sizeof(int&) << endl;//4
cout << sizeof(double&) << endl;//8
//cout << sizeof(void&) << endl;
return 0;
}
B.指针可以不初始化 但是引用必须初始化
int *p;//OK
int& r;//Error
C.指针可以修改目标(指向不同的地址) 但是引用不能修改引用方向
int *p=&a;//指向a
p = &b;//指向b
但是引用:
int& r = a;//引用a
r=b;//对a进行赋值了变成
D.可以声明void指针 void* 但是不能声明void引用 void&
void* p;//可以
void& r=v;//错误
E.可以声明指针的指针(二级指针) 但不可以定义引用的引用
int **p;
int a; int&r = a;
! int&& rra=r;//引用的引用不行
int& rra =r;//这样可以,还是a
F.可以定义指针的引用,但不可以定义引用的指针
int *p;
int*&rp = p;//指针的引用可以
int &r=a
! int& *pr=&r;//引用的指针不可以
G.可以定义数组的引用 但不能定义引用的数组
数组的引用 本质是引用 引用了一个数组
int arr[5]={1,2,3,1023,1024};
//数组的指针 int (*arr)[5]
int (*par)[5] = &arr;
//引用
int (&rar)[5] = arr;
cout << arr << endl;
cout << rar << endl;//同一个地址
cout << rar[0] << endl;
!//引用的数组
int a,b,c,d,e;
int& brr[5]={a,b,c,d,e};//错误
C++的类型转换
隐式类型转换 自动类型转换
C++中,int* ----> void* 隐式类型转换
void* ---> int* 不能隐式类型转换
基础数据类型之间都可以进行隐式类型转换
强制类型转换 (int)3.14
(目标类型)(源对象)
C++提供了四种显示类型转换运算符
1.静态类型转换(能够隐式转换的)
static_cast<目标类型>(源对象)
如果目标类型和源对象类型在某一个方向上可以进行隐式类型转换,
那么在两个方向上都可以进行静态类型转换;
反之,如果在两个方向上都不能进行隐式类型转换,
则任意一个方向上都不能进行静态类型转换
类类型定义自定义转换规则,也可以使用静态类型转换
#include <iostream>
using namespace std;
int main(){
int x = 111;
double d = 3.14;
double ox = static_cast<double>(x);
int od = static_cast<int>(d);
void *p = &x;//int* ---> void*
//int *pi = p;//void *不能隐式转换成int*
int *pi = static_cast<int*>(p);
double *pd = &d;
//pi = pd;
//pd = pi;
//pd = static_cast<double*>(pi);//int* --- double* 两个方向上都不能进行隐式类型转换 所以不能用 static_cast
return 0;
}
2.去常属性类型转换
const_cast<目标类型>(源对象)
只能去除指针(成员指针)和引用的常属性
C语言中,局部变量用const修饰,表示只读,可以通过指针进行修改该变量
C++中,局部变量用const修饰,那么该变量是常量 在编译时会直接用该值直接替换首先我们先对C++中的const特性做一个验证
#include <iostream>
using namespace std;
int main(){
const int x = 1024;//C++对const进行了优化
int *p = (int*)&x;
*p = 9527;
cout << x << endl;//1024 还是没修改,C语言理论上修改了,但是C++不行
cout << *p << endl;//9527
cout << &x << endl;//0x61fe14
cout << p << endl;//0x61fe14 虽然地址上也一致,但就是没有修改
cout << *&x << endl;//9527,这样写能得到9527,但是x值没变
return 0;
}
去常属性示例
#include <iostream>
using namespace std;
int main(){
const int x = 10;
//int y = const_cast<int>(x);//既不是指针也不是引用,这个会报错,因为去常只能用在引用或者指针
const int& r = 10;
//int& ri = x;//不行 丢失const属性
int& ri = const_cast<int&>(x);// const int& 引用的常属性
ri = 1111;
cout << x << endl;
cout << ri << endl;
int *p = const_cast<int*>(&x); //去除指针的const属性
return 0;
}
3.动态类型转换
dynamic_cast<目标类型>(源对象)
用在具有多态性的父子类指针或引用之间
4.重解释类型转换
reinterpret_cast<目标类型>(源对象)
用在指针与指针之间 或者 指针与整数之间 做类型转换
#include <iostream>
using namespace std;
int main(){
int x = 10;
double d = 3.14;
int *pi = reinterpret_cast<int*>(&d);
double *pd = reinterpret_cast<double*>(&x);
pi = reinterpret_cast<int*>(x);
/*
代码很明显是错误的,因为变量a,从本质上来说,无法转化为一个指针类型。
但是在编译的时候,并不会报错。reinterpret_cast 是最不安全的类型转换函数,因为编译器不会检查转换时,参数类型是否合理.
*/
return 0;
}
C++指针类型转换
1.指针 可以隐式转换成 void*
2.void * 可以用static_cast 静态类型转换成其它类型的指针
3.不同类型的指针可以用 reinterpret_cast 重解释类型转换
4.强制类型转换
5.多态性的父子类型指针之间可以用 dynamic_cast 动态类型转换
C++之父的建议
1.少用宏,多用const、enum和inline
2.变量随用随声明同时初始化
3.少用malloc/free,多用new/delete
4.少用强制类型转换,建议用显示类型转换替代
5.少用C风格的字符串,多用string
6.树立面向对象的编程思想