1.C++简介
C++起源:带类的C语言,C++支持3种程序设计方式:过程化程序设计(比如C),面向对象程序设计,泛型程序设计(对变量的数据类型模糊化);
过程化程序设计,类似数学,即算法+数据,用程序语言描述处理数据的过程,为了优化程序的结构,出现了结构化程序设计(自顶向下的设计,程序结构划分为3种:顺序,分支,循环);C语言支持结构化程序设计,C语言可以操作到位,更接近硬件,过去的高级语言通常只能操作到某个变量(在内存中操作)。
面向对象:允许我们设计与问题对应的类。并且提高代码复用性。
泛型程序设计:对于C语言,我们必须规定每个数据的类型,C++可以对数据类型模糊化,统一为T类型。
2.程序生成(创建源码,编译和链接)
源码是程序员写好的程序,用后缀名区分源文件,cpp后缀表示C++源文件,c后缀表示C的源文件。创建源码的方式:在编辑器编辑源文件。现在大家都使用IDE开发,将编辑,编译,链接过程集成到一个软件中。 在VS中,一个软件被称为一个解决方案,解决方案可以包含多个项目(解决方案是项目的容器),每个源文件都是项目的组成部分。 编译:将源文件翻译成机器语言,称为目标文件,目标文件不是可运行的文件; 链接:将目标文件与库的目标文件捆绑,形成可执行文件。 编译链接的方式取决于操作系统和编译器。比如windows+gcc,在命令行输入gcc prog.cpp就能实现编译和链接。在VS中,可以直接”生成解决方案”。
3.进入C++
首先看一个C++的程序示例:
两种注释: // 和/*……*/
预处理指令用#开头,编译时先处理预处理指令,再执行编译,比如:#include <iostream> 相当于在此处插入iostream的内容,iostream被称为头文件。通常,每个头文件支持一组工具(头文件是库的接口),iostream提供输入输出工具,比如cout输出,传统C++和C风格的头文件后缀为h(stdio.h),新C++风格的头文件不需要后缀。
命名空间:编写大型程序或将多个厂商的代码整合到一起需要的工具,不同厂商的代码中可能有同名的变量和函数,每个厂商的产品封装在自己的命名空间下(ns1::x 和ns2::x )。新C++的标准组件都在命名空间std下(std::cout )。
using编译指令:通知编译器,程序中的标准组件都是某一个命名空间中,不在需要用”命名空间::组件名” 的方式,可以直接用”组件名”调用组件。
main函数:C++程序有很多函数组成,但main函数才是执行入口。 函数头:函数的接口,main函数是程序与操作系统之间的接口,函数头由”函数名,返回值,参数” 组成,比如int main() 花括号内的是函数体,由一组语句组成,表示函数应该执行哪些计算机指令。 对于cout<<”hello”<<endl ,<< 表示信息流向,hello流向cout,endl流向hello,endl代表重启一行(光标移动到下一行的第一列,也可以用字符’\n’ 表示)。
4.C++语句
同样看下面示例: 多了一个变量:程序运行中可以变化的值,变量必须占有存储空间。 变量定义:为变量准备存储空间,为编译器检查变量使用是否正确提供依据。定义格式:”类型 变量列表” ,比如int carrots 。变量必须先定义再使用。
赋值语句将某个值存放到内存单元,carrots=25,将25存放到carrots对应的内存单元,carrots=carrots-1将carrots对应的内存单元内容减1,再存放到carrots的内存单元。 赋值运算符= :二元运算符(两个运算对象,左右各一个,计算右边表达式的值,存放到左边),右结合的(先执行z=0,再执行y=z,再执行x=y;先执行carrots-1,再执行carrots= carrots-1),比如:
int x, y, x;
x=y=z=0;
cout可以输出变量值:cout<<carrots ,其实cout很复杂,它可以取出carrots的内容取出,整数25再转换成字符串”25”输出到显示器。cout可以拼接输出流:cout<<”carrots:”<<carrots<<”end”<<endl ;
我们可以在运行中用键盘赋值变量,使用cin:
cin>>变量; 或者:
cin>>变量1>>变量2;
键盘输入变量1的值,回车,再输入变量2的值,再回车,执行后面语句。cin示例如下:
5.函数入门
函数是程序中的一个零件,可以完成一个功能。
返回值类型 函数名(参数列表)
{
语句组合
}
函数可以没有参数,括号内为空或void,函数可以没有返回值,返回值类型用void表示。
函数原型声明:告诉C++编译器,函数的参数和返回值,使编译器可以检查程序对函数的用法是否正确,比如:double sqrt(double); ,函数原型声明出现在每个源文件的开头。
我们可以从库(我们需要include头文件)中调用函数,头文件中的内容是该库中所有函数的原型声明;因此include就是把原型声明插入源文件中。 函数定义 设计函数头:根据函数的输入输出设计参数和返回值,并给函数命名; 设计函数体:从参数到返回值的计算过程,使用return返回值并退出函数;
无返回值函数实例: 有返回值的函数示例:
6.整型
整型是高级语言处理整数的工具(子集)。 比如unsigned short,也至少占16位,但是由于视为无符号数,则数值的最大值变得更大了,因为最小值变成了0,同样的占位空间获得了更大的数值范围。 整型变量声音如:short score 或 short int score;int number1, number2;
查看整型占有空间的大小: 方法一是sizeof(一元运算符)输出字节占用数:sizeof(类型名或表达式),即sizeof(int), sizeof(score) 方法二是查看头文件climits,该头文件中定义了各种类型长度的符号常量: 变量在定义时,初始值是随机的(值是创建变量前,该内存块原有的值)。所以为了避免出错,我们最好在变量定义时赋值: int score=90 初值可以是常量或一个已经定义的变量: int scoreAvg=80; int score=scoreAvg;
整型的字面量通常为:十进制,八进制(以0开头,比如0127),十六进制(以0x开头,比如0x18FD) C++在处理数值时,统一转换为二进制处理,输出时统一转换为十进制。
对于整型常量,默认为int,超出范围则视为long,超出long则视为long long。我们也可以用后缀明确指出常量类型,比如100L(长整型);其实,对于int scoreAvg=80; 80就是一个整型常量。
7.char,bool(小整数)
char是用于处理字符的类型:字符在机器内用8位整数编码(编码规则:ASCII,ASCII编码的特点是大写字母编码连续,小写字母编码连续,数字字符编码连续) 编程时,不需要关心字符的编码:字符输入(cin>>字符型变量),字符输出(cout<<字符类型变量或字符常量)
字符类型(char类型)的运算:在早期内存昂贵时候,由于char只占一个字节,程序员常将char作为比short小的整型使用,char也可以执行加减和比较运算,参加运算的是内码值。 char类型常量: 常规字符:用单引号’A’, ‘s’ 转义字符:’\n’ 换行,’\t’ 水平制表符
char类型示例: bool类型:表示逻辑”真”和”假”,bool类型的值(true和false) bool类型的机器内表示为一个字节,true是1,false是0,bool可以作为算术运算的运算数。 bool不能直接输入输出,直接输出bool类型的值得到的不是true或flase,而是1或0。
8.const与符号常量
符号常量:为程序中的常量取一个名字,称为符号常量;使用符号常量提高了程序的可读性,并且便于管理常量值。
符号常量的定义 用#define定义:#define 符号常量名 字符串(不建议使用这种C的定义方式); const限定符:限定一个变量是只读的,(const 类型名 符号常量名 = 初值; ),必须有初值,比如: const double PI=3.14; 符号常量的命名与变量命名相同,为方便区分,符号常量名采用大写。
9.浮点数
浮点数即实数R,浮点数名称的来源:小数点可以移动,比如3.14可表示为
314.0
?
1
0
?
2
314.0*10^{-2}
314.0?10?2或者
0.314
?
10
0.314*10
0.314?10
浮点数的表示,浮点数在机器内用二进制表示,我们需要存储两部分,比如
10100
?
2
1101
10100*2^{1101}
10100?21101,我们分别保存10100和1101,即尾数和指数; 浮点常量的表示 十进制与日常表示相同,比如127.7。 科学计数法,尾数E指数 或者 尾数e指数,比如34E-8,-1.5e10,科学计数法用于表示很大或很小的实数。
浮点常量默认是double,我们也可以明确指定浮点常量的类型,比如1.5F(float)与 2.3E10L(long double)
浮点类型的精度示例: 可以看出float确保7位精度,double则确保15位精度。
10.算术表达式
算术运算符: 优先级 高:乘,除,取模 低:加,减 我们可以用括号自己选择优先级
同一优先级,算术表达式满足左结合性,先计算左边的运算,再计算右边。
除法的结果取决于运算数:两个整数相除结果为整数,只要有一个运算数是浮点数,则会保留小数部分。 C++在实际执行时,并不能操作不同类型的数据,前面提到的除法只是被做了类型转换才得以实现。C++本身只能操作int和int,double和double,float和float等等。
自动类型转换出现的场合 1.赋值或初始化时,如果右边的表达式计算结果类型与左边的变量类型不同:右边值被转换成左边变量类型; 2.函数参数传递时,实际参数被转换成形式参数的类型; 3.表达式中的运算数类型不一致,需要遵循转换规则:把精度小的运算数向精度大的运算数转换;
自动类型转换的示例: 我们也可以进行强制类型转换: C++的风格为:类型 (表达式) C的风格为:(类型) 表达式
11.数组
数组用于存储多个同类型的值; 定义格式为:类型 数组名[元素个数] 或者 类型 数组名[元素个数]={初始值表} 比如: int array[10]; 数组的元素初值为随机值 double darray[3]={1.1,2.1,3.4} int iarray[]={1,2,3}; []为不指出元素个数情况,该数组有三个元素 short sarray[10]={0,1,1}; 三个初值给了数组的前三个元素,后面的元素初值统一为0
数组元素的索引:数组名[下标],比如array[5]可以访问第6个元素 编译器不检查下标的合法性,所以要注意数组的范围,不然会无意修改了内存其他区域的值。
数组示例:
12.C风格字符串
“programming”就是一个字符串常量,看起来是11个字符,但其实是12个字符,因为C语言中规定字符串以’\0’(空字符)结束。 C用char型数组保存字符串,“programming”由12个元素的数组存储。 如: char str[]={‘s’, ‘t’, ’r’, ‘i’, ‘n’, ‘g’, ‘\0’} 为了使用简单,C也支持char型数组的简便形式赋值: char str[]=”string” 或 char str[10]=”string”(剩余元素值为0,即在char型下是空字符’\0’) 注意,字符串是一个字符数组(char型数组),但字符数组不一定是字符串,字符串必须以’\0’结束。 char cArray[]={‘s’, ‘t’, ’r’, ‘i’, ‘n’, ‘g’} 只是一个字符数组,但不是字符串,空字符串“”虽然没有内容,但是也占用一个字节,存放空字符’\0’。 字符串的输出可以使用cout,cout逐个输出字符串中的字符,直到遇到’\0’: cout<<字符串常量 cout<<字符串变量(字符型数组,并且以’\0’结束)
字符串的cin输入
对于输入一个单词: cin>>字符数组名,比如cin>>str,cin以回车字符或者空格字符作为输入结束的标记
对于输入一行: cin.getline(字符数组名,数组规模) cin.get(字符数组名,数组规模) 以回车字符或达到数组规模结束输入,区别:getline将回车的换行符丢弃,get会将换行符留在缓冲区放在下一次输入的最开始位置。
在早期C语言没有getline时候,只能使用get,但是get对于读入回车的处理会让人们对字符文本的逻辑容易出错,为了让get每次都只输入一行,并让回车不放在下一次输入的行中,我们使用无参数的cin.get(),cin.get()读入任意一个字符,包含回车。于是常见的输入格式为: cin.get(str1,80); cin.get(); //读入cin.get(str1,80);中的回车换行符 cin.get(str2,80); //此次输入的最开始就不再有换行符存在
字符串示例: 如果把cin.get()注释了: 我们发现不能输入address,这是因为,当我们输入year后,还有一个回车,这个回车被保留在输入队列,cin.getline(address,80)首先读入回车,误认为已经结束输入。
关于cin>>变量1>>变量2; 键盘输入变量1的值,回车(或者空格),再输入变量2的值,再回车(或者空格),才能执行后面语句;对于cin>>的方式,每一次执行>>前,都是变量值与空格放入缓冲区,变量值被赋值给变量,空格在输入队列中,下一次执行>>时,空格从队列中移除,队列中又是新的变量值+空格,然后变量值被赋值到下一个变量。 比如我们cin>>year; 我们输入1990,然后回车,1990与回车就被放到输入缓冲区中(队列),我们必须回车了机器才能将1990赋值给year,队列中剩下空格字符;
cin.get和cin.getline都是从缓冲区(输入队列)中读入,键盘的输入也是先进入队列,才被get和getline读到。
13.C++风格字符串
C语言没有字符串类型,所以操作起来是不方便的,因此C++出现了string类型; string类型:专门处理字符串的类型,在头文件string中,在命名空间std下 为了输入带空格的字符串,应该使用std::getline(cin, str),注意不是cin.getline
字符串示例: 可以看到,std::getline(cin, str)也是从缓冲区读,也是先读入了cin>>处引入的空格字符,如果我们在cin>>s1时,最后采用换行,那么getline将读到换行,误认为结束。
14.结构
结构类型:将描述一个复杂对象的多个部分组成一个整体
建立结构声明:描述结构的组成
struct book{
char title[MAXTITL];
char author[MAXAUTL];
float value;
}
我们可以定义结构变量,也可以定义结构数组:book book1; book bookarray[10]; 结构示例: 结构的成员可以只占若干位,也被称为结构的位字段,位字段的定义为: 类型 变量 : 位数(即bit) z指定的位数决定了结构体变量d的大小,当z:29时,占用4个字节,共32位;此时,x,y,z相当于共用一个int变量(1个int刚好32位); 当z:32时,需要使用8个字节,因为结构此时占用35位,需要根据成员类型自动补齐,35位超出1个int的空间,所以要两个int,一个用于存储z,另一个用于x,y的共享; 从int的视角看,成员存在共享,但从bit角度看,每个成员占用的位是不同的。
15.指针
指针:值为内存地址的变量,指针提供了间接访问的方式;
定义指针变量:类型名 *指针变量名 ,比如int *p ;
运算符&:一元运算符,运算对象是变量,运算结果是变量的地址。
int *p, a;
p=&a;
间接访问运算符* :一元运算符,运算对象是指针,运算结果是指针指向的变量,*p=*p * 2 ;
指针示例: 指针的注意事项:
16.动态内存分配
程序通过声明变量通知计算机为它准备好内存空间,这些信息是编译时就能确定的。每个变量对应一块内存空间。变量名是这块空间的名字,程序通过变量名访问对应的空间。
有时候编程时,我们不必为所有变量进行声明。我们可以采用动态内存分配节约空间。 动态内存分配:在程序运行时申请内存和释放内存的功能。
动态内存申请:在内存寻找一块大小合适的空间,返回起始地址。注意,动态分配的内存没有名字,只有地址,只能间接访问。(这也是C++需要指针的一个原因)
动态内存的申请方法:
类型 指针=new 类型
类型 指针=new 类型 [结果值为整数的表达式]
对于int *array = new int [n] ,实现了动态数组。
内存释放:通过变量声明获得的内存会在存储期结束时回收。但通过动态申请的空间不会被系统自动回收。 释放空间的运算:
delete 指针
delete [] 指针
delete的重要性:没有delete的动态内存一直被程序占用,特别是,当程序执行结束,这块空间依然被标识为被使用,这被称为内存泄漏。
动态内存示例1,可以看出,指针只是一个用于保存地址的变量,不管指向什么类型的变量,该指针都占用8字节(与操作系统64位有关),由于指针是变量,我们也能够对指针变量取其地址: 动态内存示例2:
17.指针运算
指针变量保存的是整数(所指变量的地址),我们可以对指针执行加减运算: 指针加1是加上一个基类型的长度; 指针减1是减去一个基类型的长度; 也就是说,具体是加减多少取决于基类的类型: 同理,我们写pi=pi+1也是一样的,pi的值增加4;
对于++a和a++:两个表达式的副效应都是令 a 增加 1 。区别在于表达式 a++ 的值是 a 自增前的值, ++a 的值是 a 自增后的值。(所以最好用++a形式)
指针与数组:数组名就是指针,因此指针运算应该与数组关联才有意义,因为我们可以利用数组名去访问内存; 但是数组名与指针变量的区别在于,数组名是指针常量,也就是说数组名(指针常量)的地址是不能修改的。
但我们可以将数组名赋给同类型的指针变量:int *ap=a ,从而我们将指针变量指向了数组的第一个变量。 通过指针访问数组的示例: sizeof不同的意义在于:注意明确数组和指针的区别,数组名虽然保存了数组首元素的地址,但数组本身是一个连续的变量组合,指针只是存放地址的变量。
18.指针与字符串
C风格的字符串使用一个char型数组存储,并以’\0’作为结束符;
由于数组名是一个指针,所以C风格的字符串可以用一个指向字符的指针表示。通常我们定义指针指向字符串(这个char型数组),并用const限定该指针变量指向的字符串只读(限制我们不能通过指针修改该字符串中的字符);
const 与指针,分为三类:pointer to const,const pointer与const pointer to const
cstring库中的函数以及用cout输出时,都是从数组名对应的地址或从指针指向的地址开始处理到’\0’结束。
字符串示例如下,注意当指针指向char时,与指向其他类型不同,所以我们需要额外留意指针与字符串的读写问题:
19.动态结构
运行时通过new申请一个动态的结构变量 注意在访问动态成员时,(*指针).成员 ,由于成员运算符优先级高于* 运算符,所以要加括号。为了避免疏忽括号的情况,我们可以使用指针运算符-> 来访问。
动态结构示例:
20.数组的替代品
C++中有两种数组的替代:vector和array
vector是一种动态数组,可以自动调整长度。 用法:包含头文件vector,定义时,需要指出数据类型和规模,规模可以是变量,不指出规模则规模为0; array是效率高于vector的数组,但规模固定,array是C++11新增的类型。 用法:包含头文件array,定义时需要指出类型和规模,规模是常量。 array和vector相比普通数组的区别在于:array,vector支持整个对象的整体赋值,使用示例如下:
|