C++简介
C++是一种静态类型(在编译时执行类型检查,而不是在运行时执行)、通用的、支持过程化编程和面对对象编程的语言。综合了高级语言和低级语言的特点。由Bjarne Stroustrup在1979年在贝尔实验室开始设计开发。它进一步扩充和完善了C语言,1983年被命名为C++。完全支持面向对象的程序设计,包含面向对象开发的四大特性:封装 抽象 继承 多态
基本概念
C++程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。
- 对象:具有状态和行为
- 类:描述对象行为/状态的模板/蓝图
- 方法:一共方法表示一种行为,一个类可以包含多个方法,可以在方法中写入逻辑、操作数据以及执行所有的动作
- 即时变量:每个对象都有其独特的即时变量,对象的状态是由这些即时变量的值创建的。
第一个C++程序
#include <iostream>
using namespace std;//告诉编译器使用std命名空间
int main()
{
cout<<"Hello world! I am"<<18<<" today!"<<endl;
return 0;
}
注释的规范
C++支持单行注释和多行注释,注释中的所有字符会被编译器忽略。C++注释可以以 /* 开始,以 */ 终止,如
/*这是注释*/
/*跨行注释
*也可以这样表示
*/
和C一样,注释也可以 // 开始,到该行行末为止。在 /* 和 /注释内部,//字符没有特殊含义,可以在一种注释内部嵌套另外一种注释。但是块注释符(/…/)是不可以嵌套使用的。
可以使用**#if 0… #endif***来实现注释,且可以嵌套使用,格式为:
#if 0
code...
#endif
code部分即被注释掉,将***#if 0***改成***#if 1*** 即可执行code部分。或者如下,condition条件为true执行code1,否则执行code2。
#if condition
code1
#else
code2
#endif
面向对象程序设计
程序=类+类+…类
设计的过程就是设计类的过程
- 将某个客观事物共同特点(属性)归纳出来,形成一个数据结构,可以用多个变量描述事物的属性
- 可以将这类事物所能进行的行为也归纳出来,形成一个个函数,用来操作数据结构,即抽象
- 通过某种语法形式,将数据结构和操作该数据结构的函数捆绑到一起,形成一个类,从而使得数据结构和操作该数据结构的算法呈现出显而易见的联系,即封装
- 抽象、封装、继承、多态是其四个基本结构
优点
- 更快更正确更经济地建立软件
- 更高效的实现函数的复用
- 更清晰的实现变量和函数的关系使得程序更易于维护和修改
引用
基本概念
某个变量的引用等价于这个变量,相当于这个变量的别名。定义引用时,一定要将其初始化成引用某个变量;初始化后它就一直引用该变量,不会再引用别的变量了;引用只能引用变量,不能引用常量和表达式,可以引用变量的引用。
定义引用并将其初始化为某个变量:类型名 & 引用名=被引用的变量名,如
int n=4;
int &r=n;//r引用了n,r的类型是int &
简单应用
- 引用可以作为函数的参数
void Swap(int &a,int &b)
{//用引用来作为参数 交换两个数的值
int temp;
temp=a;a=b;b=temp;
}
Swap(n1,n2);//即可将n1和n2的值交换
- 引用可以作为函数的返回值
int n=3;
int &SetValue(){return n;}//引用作为函数的返回值,类型为int &
int main()
{
SetValue()=40;//对函数调用返回的结果进行调用,等价于对n进行赋值
cout<<n; //此时n值为40
return 0;
}
- 常引用:定义引用时,前面加上const关键字,即为常引用,如
int n=100;
const int &r=n;//r的类型为const int &
r=200;//error,不可以通过常引用修改其引用的变量的值
n=300;//ok,被引用的内容可以通过其他方式修改
const T&和T &是不同的类型。T&类型的引用或T类型的变量可以用来初始化const T&类型的引用。const T类型的常变量和const T&类型的引用不能用来初始化T&类型的引用,除非进行强制类型转换。
C++常量
常量是固定值,是特殊的变量,值在定义后不能进行修改。常量类型有:整型数字、浮点数字、字符、字符串、布尔值等。
整型常量
可以是十进制、十六进制、八进制的常量,可以根据前缀判断:0x或OX表示十六进制,0表示八进制,不带前缀表示十进制。整型常量可以带一个后缀,U(unsigned)表示无符号整数,L(long)表示长整型,大小写均可。如
> 88 //十进制
>0123 //八进制
>0x5b //十六进制
>30 //整数
>30l //长整数
>30u //无符号整数
>30ul //无符号长整数
浮点常量
浮点常量由整数部分、小数点、小数和指数部分组成。可以表示成小数形式和指数形式。用小数形式来表示时,必须包含整数部分和小数部分或同时包含两者。指数形式必须包含小数点、指数或同时包含两者。
3.14 //合法
3.14E-5L //合法
123E //非法,指数部分不完整
210f //非法:没有小数部分或指数部分
.e123 //非法:没有整数或分数
布尔常量
共有两个,true值为真值,false值为假,不能把true看成1,把false堪称0.
字符常量
被括在单引号中。若以大写L开头,则其为一个宽字符常量,如L‘M’,必须存储在wchar_t类型的变量中,反之为窄字符常量,可以存储在char类型的变量中。字符常量包括普通的字符、转义字符或通用的字符,如‘\u02C0’.
字符串常量
被括在双引号中。一个字符串包括普通的字符、转移序列和通用的字符。
定义常量
在编程实践中,习惯性的把常量定义为大写字母形式。两种定义方式:
使用 #define 预处理器
宏定义是可以取消的,是直接替换,不会分配内存,不能作为参数传递给函数。
#define A 10
#define B 20
#undef A //取消A之前的常量定义
#define A //重新进行定义
使用const关键字
可以使用const前缀声明指定类型的常量,定义成const后的常量,程序对其只能读不能修改。
- 定义常量
const int A=10;
const char B='\n';
- 定义常量指针
不可以通过常量指针修改其指向的内容,但是常量指针指向的内容是可以被修改的。
int n,m;
const int *p=&n;
*p=5; //编译出错
n=4; //√
p=&m; //√,常量指针的指向可以变化
不能把常量指针赋值给非常量指针,反过来可以。
const int *p1; int *p2;
p1=p2; //√
p2=p1; //×,常量指针被赋值给了非常量指针,可以通过p2来改变p1,本质上错误
p2=(int *)p1; //√,强制类型转换
函数参数为常量指针时,可以避免函数内部不小心改变参数指针所指地方的内容
void MyPrint(const char *p)
{//p为常量指针,指向的内容不应该被修改
strcpy(p,"this"); //编译出错
//strcpy第一个参数为char型,不能用const char类型给char型赋值,所以报错
printf("%s",p); //ok
}
- 定义常引用
不能通过常引用修改其引用的变量,但可直接修改被引用的变量的值
int n;
const int &r=n; //定义常引用r,引用n
r=5; //error,不能通过常引用修改被引用的变量值
n=4; //ok,可以直接修改被引用的变量值
动态内存分配
用new运算符实现动态内存分配
- 分配一个变量:P=new T 分配一个变量,T是任意类型名,P是类型为T*的指针,动态分配出一片大小为sizeof(T)字节的内存空间并将该内存空间的起始地址赋给P
int *pn;
pn=new int;//分配内存空间
*pn=5; //往空间里写入5
- 分配一个数组:P=new T[N] 动态分配一片大小为sizeof(T)*N字节的空间,并且将该内存空间的起始地址赋值给P
int *pn;
int i=5;
pn=new int[i*5];//动态分配内存
pn[0]=20;//ok,在第一个空间里写入数值
pn[100]=30;//error,编译时没有问题,运行时导致数组越界
- new运算符的返回值类型
new T;//返回类型为 T*
new T[n];//返回值类型为T *
int *p=new int;//两边类型匹配
delete
用new动态分配出的内存空间,一定要用delete运算符释放动态分配的内存,否则就会一直被程序调用,占用的空间越来越多,导致系统崩溃。
用delete释放动态分配的数组,要加 [ ],不加中括号不会报错,但是会导致申请出的空间不能被完全释放产生浪费。
int *p=new int;//分配内存
*p=5;//向申请来的空间里存放数值
delete p;//delete指针,该指针必须指向new出来的空间
delete p;//error,不可以重复delete
int *p=new int[20];//动态申请数组空间内存
p[0]=1;
delete []p;//delete指针:该指针必须指向new出来数组
内联函数/重载函数/参数缺省
内联函数
函数调用是有时间开销的,如果函数本身只有几条语句,执行很快,且函数被反复执行很多次,相比之下调用函数产生的开销比较大,为了减少函数调用的开销,引入内联函数机制,编译器处理对内联函数的调用语句时,是将整个函数的代码插到调用语句处,而不会产生调用函数的语句。但同时也带来函数体积增大等坏处。
在函数定义前加上inline关键字,即可定义内联函数。
inline int Max(int a,int b)
{//inline定义内联函数,返回两数之间的较大者
if(a>b)return a;
else return b;
}
函数重载
一个或多个函数,名字相同,参数个数或参数类型不同,即为函数重载。函数重载使得函数命名简单化。编译器根据调用语句中的实参个数和类型判断应该调用哪个函数。若两函数名字相同,参数表也相同,但返回值的类型不同,那不是重载,是重复定义,要区分清楚❌
int Max(double f1,double f2){}
int Max(int i1,int i2){}
int Max(int i1,int i2,int i3){}
Max(1,2);//调用第二个函数
Max(1.1,2.2);//调用第一个函数
Max(3,2.4);//error,二义性
参数缺省
定义函数时可以让最右边连续若干个参数有缺省值,则调用函数时若相应文职不写参数,参数就是缺省值。
缺省参数可以提高程序的可扩充性,若某个写好的函数要加新的参数,而原先的那些调用该函数的语句未必需要使用新增的参数,则为了避免对原先函数调用语句的修改,可以使用缺省参数。
void func(int x1,int x2=2,int x3=3){};//定义缺省函数
func(10);//ok,等效于func(10,2,3);
func(10,8);//ok,等效于func(10,8,3);
func(10,,8);//error,只能最右边连续有若干个参数缺省