C++应用方向
-
C++头文件都是模板编写,没有.h,而模板有一个特点必须知道所有实现之后才能进行正常编译
(cd /usr/include/c++)
-
函数声明和定义
(形式上区别,花括号和末尾分号
声明可以有多次,定义只能有一次)
-
<<输出流运算符,左移乘2 endl换行
-
>>输入流运算符,右移除2
-
同窗口无法编译运行
-
vimplus中可使用gcc注释一行,u还原或者再按一次
-
运算符重载,视作函数
命名空间
为什么要使用命名空间?
- 解决函数命名冲突
(命名冲突是指同一个作用域内有多个同名实体)
命名空间是什么?
- namespace 名字{ }
(末尾不用加分号,其中可以定义变量、常量、函数、结构体、类、模板、命名空间,统称为实体)
如何使用命名空间?
用法1 :using编译指令
-
using namespace std;
(可以一次将std命名空间中实体全部引出,可能导致自己定义的实体和std中实体冲突)
用法2 :作用域限定符
-
wd::
(每次使用实体时加上,即使自定义的实体与std命名空间中的实体冲突也没问题,但使用起来麻烦)
-
::
(匿名命名空间,如只有一个printf不需要加作用域限定符也可以正确使用)
用法3 :using声明机制
-
using std::cout
(一次只将命名空间中的一个实体引出,自然写代码时就不会定义同名函数)
两个命名空间内互相调用
-
type wd::func()
(带命名空间的函数声明,结构体不能这样使用)
-
命名空间可以扩展,先写一部分,中间被隔开,后写一部分
(包括标准命名空间,一般std里是小写,但不建议)
命名空间的嵌套
-
wd::wh::
(在一个命名空间中再定义另一个命名空间,调用内部实体时要写全作用域)
const常量
- 可以创建常量,定义时必须初始化,不能进行二次赋值
用法1:修饰变量
-
#define MAX 10
(宏定义,发生的时机在预处理阶段,做字符串替换,不做任何类型检查,若产生bug直到运行时才出现)
-
const
(发生的时机在编译阶段,会执行类型检查,若产生bug会立即报告)
-
const int number=10;
-
int const number=10;
(以上两种都可)
-
用法2:修饰指针
-
关于指针,我们只关注其本身和所指的内容
-
修饰谁谁就不能变,双重const则都不能变
-
优先级:()>[]>*
常量指针
-
const int * number=10;
-
int const * number=10;
(当const位于*左边的时候,不能改变指针所指的值,但是可以改变指针本身指向)
指针常量
-
int * const number=10;
(当const位于*右边的时候,不能改变指针本身指向,但是可以改变指针所指的值)
函数指针与指针函数
-
int (*pf)(int x)
(指向返回类型是int,参数是int的函数)
-
int* pf (int x)
(函数的返回类型是指针,参数是int)
数组指针与指针数组
-
int (*parray)[]
(指向含有[]多个元素的一维数组,二维数组时也叫行指针)
-
int* parray[]
(数组里面都是指针,可以用来存放变量地址,需要分别赋值)
用法3:对成员函数的修饰
- 见类
用法4:对对象的修饰
- 见类
开辟和回收堆空间
C中空间的使用malloc/free表达式
步骤1:申请堆空间
-
int *pInt = (int *)malloc(sizeof(int));
(严格来说应该判断是否申请成功,即是否空指针)
步骤2:清零(初始化)
-
memset(pInt,0,sizeof(int));
(sizeof是运算符)
步骤3:赋值使用
- *pInt=10;
步骤4:回收(释放)堆空间
-
free(pInt);
(打印地址时printf中用%p)
C++中空间的使用new/delete表达式
步骤1:申请堆空间并初始化,赋值
-
int *pInt = new int(10);
-
int *parray = new int[10]();
(对于数组此处不可以赋值)
步骤2:回收堆空间
-
delete pInt;
-
delete [] parray;
(中括号中间不能填数字,以免被视为偏移,在开辟空间时已经默认多开了4个字节来存数组长度、标志位之类的信息)
-
size_t 类型表示C 中任何对象所能达到的最大长度。它是无符号整数
两者的区别与联系
-
malloc/free是C中的库函数,而new/delete是C++的关键字
-
malloc只能申请原始的堆空间,new申请堆空间并进行( )初始化
-
都是用来申请堆空间
-
都要成对使用,否则会造成内存泄漏
-
内存泄漏:分配了内存而没有释放,逐渐耗尽内存资源,导致系统崩溃
-
内存越界:向系统申请一块内存,使用的时候超出了申请的范围
-
内存踩踏:也叫内存重叠,一般是在拷贝过程中目的地址范围和源地址范围有重叠导致的。
-
内存溢出:想要分配的内存超出了系统能够提供的
-
野指针:指向被释放的或者访问受限内存的指针
-
&引用
-
int number=10;
int &ref=number;
(已定义变量的别名,引用的提出就是为了减少指针的使用)
实质
-
int *cosnt
(指针常量)
-
引用定义时必须初始化,一经绑定就不能改变指向
-
操作引用与操作变量本身是一样的
-
&跟了类型的是引用,否则是取地址
-
const int &a=10;
(当既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变时,就应该使用常引用)
用法1:作为函数参数
-
引用传递和指针传递不同于值传递,它们不需要进行复制,而值传递的形参只是实参的一个副本
-
引用传递相比于其他两个不会额外占用栈空间,
用法2:作为函数返回类型
-
可以对函数返回值赋值
-
函数返回引用的前提是实体的生命周期大于函数的生命周期
-
不要返回局部变量的引用,其生命周期在函数调用完时已结束
-
返回堆空间的引用,可能有内存泄漏的隐患(new/delete未成对出现),除非有内存回收的机制
-
可使用引用绑定函数int &ref=func();
-
之后再delete &ref;
(语义无错,但是不推荐这种写法)
-
指针和引用的区别与联系
相同点:
- 都是地址的概念;
(指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名)
区别:
-
指针是一个实体,而引用仅是个别名;
-
引用使用时无需解引用(*),指针需要解引用;
-
引用只能在定义时被初始化一次,之后不可变;指针可变;
-
引用没有 const,指针有 const;
-
引用不能为空,指针可以为空;
-
“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
-
指针和引用的自增(++)运算意义不一样;
-
从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
强制转换
-
(tye) a;
上面是C的写法,C操作简单,但C++更加安全
用法1:static_cast<type>(a)
-
适用范围:
-
基本数据类型之间
-
void指针转换为目标类型指针,但不安全
-
任何类型转换为void
-
用于基类和子类之间指针或引用的转换
(进行上行转换(把子类的指针或引用转换成
基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类
型检查,所以是不安全的。)
-
-
grep -rEn “*_cast” ./
(可以查看是否使用强制转换)
int inumber=10;
float fnumber=12.34;
inumber=(int)fnumber;//C
inumber=int(fnumber);//C++
inumber=static_cast<int> (fnumber);//C++
void *pre1=malloc(sizeof(int));
int *pre2=static_cast<int*>(pre1);//C++
delete pre2;
pre2=NULL;//指针置空
用法2:const_cast<type>()
- 去掉常量属性,将常量指针转化为非常量指针
const int num=100;
int *p2=const_cast<int*>(&num);
*p2=200;//C++未定义的行为
用法3:dynamic_cast<type>()
- 用于基类和派生类之间的转换,尤其是下行
用法4:reinterpret_cast<type>()
- 用于处理无关类型之间的转换,容易导致程序不安全