1.Visual C++的基本概念
(1)标识符:
以字母、数字、下划线组成的字符序列,第一个字符必须是字母或下划线。
不能与关键字同名,且不含空格标点符号和其他字符,同时区分大小写。
例: 不正确的:enum、int、2b、 a+3、a-3、#include 等均不是正确的标识符
正确的:_32、include、Int、a_3、abc
(2)数据类型:
字符型(char,1)、整型(int ,4)、实型(float,4), 双精度(double,8),无值型(void,0)、布尔型(bool,1)
int之前还可以用unsigned、signed、short、long修饰,表示不同字节的整数类型
char之前还可以用unsigned、signed修饰
(表达式的数据类型以整个表达式中最高的数据类型为准)
例:’1’+’2’、’1’+2、18/3+5. 、18+5.0/2,注意常用数学函数的返回值类型
(3)常量:(字面常量、标识符常量)
整型常量——有十进制、八进制(以0开头)、十六进制(0X(x)开头)三种形式;
实型常量——有普通和科学计数法两种形式。科学计数法时,e的前面必须要有数字,且在e之后必须是整数;(注意2.5f和2.5的区别,注意2.0和2.)
字符型常量——必须以单引号引起来,转义序列以“\”开始,用数值描述时有\ddd和\xhh两种形式,即只能是一个八进制或十六进制的数,取值范围在0~255,且十六进制必须以x开头。
字符串型常量——用双引号括起来,存储时系统默认在结尾添加“\0”。区分sizeof(字符串)和strlen(字符串)的值。
标识符常量的定义—— #define a 4
常变量的定义——const int a=4;
注意标识符常量和常变量两者的异同点
(4)变量:
需遵循“先定义后使用,先赋值再操作”的原则。
(5)算术运算符:(详见书,几个需要注意的如下)
“/”运算符:两边都是整型,则做整除运算(去尾取整),有一个是实型,则是真正的商。
“%”运算符:取余,其两边都必须是整型数。
(6)关系运算符:
关系成立,表达式结果为整数1,否则为整数0。
(7)逻辑运算符:! && ||
非0为真,用整数1表示,0为假。注意逻辑运算表达式计算时的优化问题(短路特性)。
(8)sizeof()运算符:用于计算某一操作数类型的字节数。
(9)赋值表达式:
=左边只能是变量,a++或a- -不能放在=左边
=两边数据类型不同时,以左边的数据类型为准
=的优先级仅高于逗号运算符
复合的赋值表达式中,将复合的赋值运算符右边的部分看作一个整体进行处理
(10)逗号表达式:
从左往右依次计算各表达式,以最后一个表达式的值作为整个逗号表达式的值。
(11)强制类型转换:
(type)表达式 或 type(表达式)
(12)优先级顺序:
单目优于双目,双目优于三目,在此基础上,算术→位移(插入、提取)→关系→位→逻辑→条件→赋值→逗号。(优先级的序号越小,其优先级越高)
(13)自增(++),自减(--)运算符
谁在前先做谁,只能对变量进行++或- -。
a++或a--的结果可理解为表达式,++a或--a的结果可理解为变量。
常与逻辑运算的短路特性合在一起考查
- 数据的输入和输出:C++语言本身没有输入输出语句。(P36)
在输入数据的过程中,如果输入的一行仅仅是一个回车时,cin把该键作为空格处理,仍等待输入数据。
需要注意的是,用cin输入数据时,实际输入数据的个数、类型及顺序,必须与cin中列举的变量一一对应,否则输入的数据不准确。列如:
int b;
char c='x';
cin>>b>>c;
cout<<b<<','<<c<<endl;
若输入a f,则输出的是0 x .
则变量b的值为0,而变量c没有获得输入的值,并使后面的cin无法正确提取数据。
(15)C++语言用非零值表示逻辑“真”值。
2.Visual C++的基本语句(顺序、选择、循环)
(1)单选语句:
if(表达式) S
表达式的值非0(成立),则执行S;否则,跳过S直接执行后继的语句。
(2)二选一语句:
if(表达式)
{ S1;}
else
{ S2;}
* 表达式可以是符合C++语法规则的任一表达式
* 表达式非0(成立),执行S1;否则执行S2.
(3)嵌套的条件语句:
if(表达式1)
{ S1; }
else if(表达式2)
{ S2; }
……
else
{ S; }
注意:else和谁配对?与最靠近它的且没有else与之配对的if进行配对
(4)条件运算符: ?:
表达式1?表达式2:表达式3
表达式1成立,以表达式2的值为整个表达式的结果,否则以表达式3的值为整个表达式的结果
(5)开关语句switch语句:
switch(表达式)
{
case 常量表达式1: S1; break;
case 常量表达式2: S2; break;
……
default: S3;
}
例:int a=7;
switch(a%5)
{case 0:cout<<a++;
case 1:cout<<++a;
case 2:cout<<a--;
case 3:cout<<--a;
default:cout<<a;}输出的结果是755.你知道是为什么吗?
int k=0;
char c='A';
do{
switch(c++){ 输出的结果是4,但是你猜猜如果把c++改成++c结果会是什么呢,没错就是2.啦啦啦
case 'A':k++;break;
case 'B':k--;
case 'C':k+=2;break;
case 'D':k=k%2;continue;
case 'E':k=k*10;break;
default:k=k/3;}k++;
}while(c<'G');
cout<<k<<endl;
* 表达式和常量表达式的值只能是整型、字符型或枚举型。
* 根据表达式的值选一个对应的常量表达式后的语句执行,直到碰到break或switch的}结束
* default的位置没有规定,但需考虑是否要添加break
* if和switch使用的注意:前者可以表示连续的条件区间也可以表示离散的条件,而后者只能表示离散的条件,因此,任一个switch结构可以转换成if结构,但反之不行。当用switch表示连续的条件时需设法将连续的条件转换成离散的形式。
(6)循环语句:
while(表达式)
S
执行过程:表达式非0做循环体S。循环体有可能一次也不被执行。
do{
S
}
while(表达式);(分号不可以忘)
执行过程:先执行循环体S,再判断表达式的值,非0继续循环。循环体至少做一次
for(<表达式1>;<表达式2>;<表达式3>) S
执行过程:
表达式1、2、3均可省略,若省略,需在合适的位置进行处理
* 三种循环可以相互转换,不管用哪种循环,一定要把握好循环条件(什么样的情况下循环)、循环体(循环做什么)
* 不能出现死循环!
(7)break、continue语句
break——跳出整个循环,不再循环(结束该层的循环)
continue——结束本次循环,进入下一次循环
3.函数
(1)C++源程序、目标程序、可执行程序的后缀名。
(2)任何一个C++程序必须有一个且只能有一个main函数。
(3)函数要先定义后使用,若先使用后定义需在使用前加函数原型说明:(绿皮书p32.T12)
列如:int f1(int,int=1);此原型声明即是对的。
int f2(int=1,int);此原型声明即是错的。
(4)函数不能嵌套定义,但可以嵌套调用(递归)。
(5)形式参数和实际参数:在函数定义中,在参数表中一一列出的参数称为形式参数(对于函数体来说,它们是已知量、要在函数体中进行处理的量,无需再重新定义);在函数调用时依次列举的参数称为实际参数(传递给函数实际处理的值或量)。即当调用函数时,实参和形参各占一个独立的存储单元。
(6)函数的返回值:
每个函数都可以有return语句。
若函数无返回值,则可不写return语句,或直接写“return;”,若函数有返回值,则必须有return语句。C++语言的函数中,return语句中的表达式的类型可以与函数的类型不一致。
一个return只能返回一个值,同一时刻只有一个return语句有效。
(7)函数的调用:(各函数之间即允许直接递归调用,也允许间接递归调用)
语句调用、表达式调用
函数调用时,实参和形参之间有传值、传址、引用三种不同的数据传递形式
(8)标识符的作用域:
*这里的标识符其实包括了变量和常量,所谓作用域则说明了标识符能被使用的范围。
*作用域分为块作用域、文件作用域、函数原型作用域、函数作用域、类作用域和命名空间作用域
*块作用域:在块内说明的标识符,其作用域始于标识符的说明处,止于块的结尾处。只能在该块内引用。
*文件作用域:在所有函数之外定义的变量称为全局变量。全局变量(标识符)的作用域。从定义位置开始到该源程序文件结束。也可通过extern将其作用域外延到包含它的工程中的其它文件。
*同一块内不允许出现同名的局部变量,不同块作用域内允许,当两个块重叠的情况下出现局部变量重名,则遵循“县官不如现管”的原则。
*局部变量和全局变量重名时,也遵循“县官不如现管”的原则。此时若想在同名的局部变量作用域内强制访问该全局变量可通过域作用符(::)访问
- 变量的存储类型:auto、static、extern、register
在C++语言中,函数的默认存储种类是extern。
在C++语言中,全局变量的默认存储种类是extern。
auto类型的变量在程序运行到定义处系统为之分配内存,在其作用域完结处系统自动回收其存储区域
static类型的量分为static局部变量和static全局变量。两者均在程序一运行时系统就为其分配内存,但它们的作用域仍遵循各自的作用域特性,static局部变量会出现虽存在但不能被访问(不在作用域范围内)的情况。
用static修饰局部变量的时,其初始化是在相关函数或语句块第一次被执行时完成的,以后再执行相关函数或语句时不再被执行。在程序执行期间,这些变量一直占用固定的存储空间,保持着最近一次的操作结果。
在程序声明的全局变量总是静态变量,其默认值是0。若在声明全局变量的时候加上static,则表示所声明的变量仅限本源程序文件内使用。
(10)内联函数:(实质是用存储空间换取运行时间)
用inline说明。在编译时将函数体嵌入到每个内联函数调用处,省去了函数调用时的参数传递、系统栈的保护与恢复等的时间开销,但增加了代码(消耗了内存空间)。
(11)函数重载:
判断重载:同函数名,形参的个数或类型不同。(与函数的返回值无关)
(12)缺省参数值的函数:
在调用函数时,若明确给出了参数的值,则使用相应参数的值;若没有给出参数值,则使用缺省值。
缺省值的给定需从最右边的参数开始。
- exit和return的使用
4.数组
(1)数组概念:数目固定、类型相同的若干个变量的有序的集合。
(2)数组定义:(数组的大小在编译的时候必须确定,在程序执行过程中不能改变,所以不能用变量说明数组的大小。)
一维数组的定义:数组名[常量表达式];
二维数组的定义:数据类型 数组名[常量表达式1][常量表达式2];
二维数组在内存中的存储:按行存放。
(3)数组的初始化:
初始化的数据个数不能超过定义的元素个数。
若是部分初始化,则余下来元素的值根据类型,整型默认为0,字符型为空……
二维数组的初始化:可以省略第一维的大小。例: int a[][3]={1,2,3,4,5,6};
数组(除字符数组外)不能被整体输入或整体输出。
不可以给数组做整体的赋值,字符数组也不可以。
(3)一维数组作为函数的参数:
若一维数组做函数的形参,则调用该函数时,相应的实参用数组名。传递给形参的是实参数组所占内存的起始位置值,这样两个数组就合用实参数组的内存单元。
(4)字符数组:元素是字符的数组。注意:字符数组中可以存放字符串也可以存放单纯的字符,是否是字符串,关键看有没有'\0'。
字符数组初始化:
char s1[3]={‘c',‘a',‘r'}; char s2[4]={‘c',‘a',‘r‘,‘\0'};
char s2[]={“car”};//字符串后自动加'\0'
char s2[]=“car”; //字符串后自动加'\0'
字符数组可以被整体输入或输出,例:char s[20]; cin>>s; cout<<s<<endl;
(5)常用字符串处理函数:#include <string.h>
strlen strcpy strcat strcmp strlwr
strupr strncmp strncpy strstr
对常见的函数的理解和记忆从这几个方面:函数功能、函数名、函数参数、函数返回值、一两个实例
5.编译预处理
文件包含(#include) 、宏定义(#define)、条件编译(#if……)
使用系统库函数,要进行对应的文件包含(注意<>和“”两者的区别)
在宏替换时,只作替换,不作运算(尤其是带参宏进行替换时)
For example:
#include<iostream>
using namespace std;
#define T(x,y) (x)<(y)?(x):(y)
void main()
{cout<<(10*T(2+4,5))<<endl;}The key:5.
6.结构体类型(struct)、共用体类型(union)、枚举类型(enum)
(1)类型定义,系统对类型不会分配内存单元,只会在用该类型定义某变量时,给该变量分配内存单元。
(2)注意区别结构体类型和共用体类型。结构体类型的变量的存储字节数为所有分量的存储字节的和值,共用体类型变量的存储字节数为其所有分量中最长分量的存储字节数。
(3)枚举类型中枚举常量及对应的整数值之间的关系,枚举型变量的输入和输出。
7.指针
对于指针的理解:
@可以把内存里的每一个存储单元想像成是一个抽屉,这个抽屉是由若干个固定大小的单位(字节)组成,
@抽屉里可以放苹果(double)、桔子(int)、葡萄(char)等,抽屉有大有小(因为苹果、桔子、梨大小不一),具体多大,就看你想放什么
@每个抽屉都有一个钥匙(指针),每个钥匙有体积和性质(两个属性。钥匙的体积(值,表现为地址编号)都是一样的,但性质类型,表现为能开多大的抽屉,以什么样的方式开)不同,不同的钥匙以不同的方式开不同的抽屉
@抽屉里可以放钥匙,由于钥匙的体积都是一样的,所以,所有放钥匙的抽屉大小肯定都是一样的,是4B
@到这里可以总结出来,抽屉有两种,放普通物品的和专门放钥匙的。
@从仓库(内存)拿出某个抽屉,给它起个名字用于放指定类型的东西(苹果、桔子、葡萄、钥匙,都有可能),就是定义一个变量(int i);
@如果上述定义的时候,指定存放的东西是钥匙,那就是定义一个指针变量(int *ip)(ip是存放钥匙的抽屉的名字)
@可以通过抽屉的名字知道该抽屉的钥匙(&i,获得的是i的指针)
@ip=&i 表示把ip指向i,即,通过ip抽屉里的钥匙可以去开名字为i的抽屉
@注意,上一句话里只是描述了可以开,实际并没有开,要想实际地去开,去取到i抽屉里的东西或往i抽屉里放东西,需要用*ip,如*ip=10;表示把10放到i抽屉里。
对于指针运算的理解:
@对于钥匙(指针),可以具体想像成是从左往右4个数字(每个数字由1B表示)组成的密码锁,不同的数字组合(指针的值)对应为同类型(存放的东西类型相同)的不同(抽屉位置不同)抽屉的钥匙
@改变4个数字的组合,让密码锁对应到不同的抽屉,就是指针的运算
@ip=ip+1,就是把ip变成紧靠其后的下一个同类型抽屉的钥匙,变之前的抽屉和变之后的抽屉位置是相邻的。可以推广到ip=ip+n;
@ip=ip-1,就是把ip变成紧靠其前的前一个同类型抽屉的钥匙,变之前的抽屉和变之后的抽屉位置是相邻的。可以推广到ip=ip-n;
@ip+n或ip-n呢?ip不变,只是相对说明其后(前)的第n个抽屉的钥匙
(1)其值为地址,其型为指针所指存储单元占有的连续字节数(所指对象的数据类型)
(2)常量指针、变量指针、函数指针
(3)指针的定义、初始化、赋值、
int a,b,*p=&a,*q; q=&b;
不同类型的指针变量所占内在单元大小相同,即sizeof(char *)、sizeof(int*)、sizeof(float*)……的值相同,均为4
(4)通过指针访问变量:*p=100;
(5)指针的运算:
算术运算:p++、p--、++p、--p、p+i、p-i、p-q
关系运算:p>q、p= =q……
*p++、*++p、(*p)++、*(p++)、++*p
(6)指针和数组:
#define N 100
#define M 10
- int *p1,*p2[N],(*p3)[N]; 注意三者的区别
- 指针和一维数组:
int a[N],*p=a;
*(p+i)óp[i]óa[i] p+ióa+ió&a[i]
p+0指向0号元素,p+N-1指向最后一个元素
- 指针和二维数组:
int a[M][N],(*q)[N]=a;
a[i][j]ó*(q[i]+j)ó*(*(q+i)+j)ó(*(q+i))[j]óq[i][j]
a+ióp+i
- 看到形如 *(指针+数值) 就替换为 指针[数值]
(7)二级指针:指针的指针,理解就行
(8)指向函数的指针:如 float (*fp)(int a,char b);
(9)返回值为指针的函数:字符串处理中常用到,拷贝、排序……
(10)new和 delete(C++提供的运算符)
type* p=new type; type* p=new type(value); type* p=new type[size];
delete p; delete []p; delete [size]p;
(11)引用:
定义引用时就必须初始化;引用类型和初始化的变量类型必须相同;定义好引用后不可再修改引用的指向;不能给常数定义引用
(12)const修饰指针(或引用):
靠近谁就const谁
float x,y;
const float *p=&x; //*p=100错,p=&y对
float * const q=&x; //*q=100对,q=&y错
const float* const r=&x; //*r=100错,r=&y错
(13)链表:创建(头节点插入、尾节点插入、有序插入)、读取、删除,在这些基本过程中均涉及到如何从一个节点移动到下一个节点。
8.类和对象(要注意区分两者的概念)
(1)面向对象的特点:封装、继承、多态
(2)定义:
- 以class开头,以分号结尾。类是数据类型,对象是变量,只是名字不同而已。
- 数据成员和成员函数
- public、private、protected(区别)
(3)构造函数
- 主要用于初始化数据成员,创建对象时被调用,形如:类名(参数){……}
- 无返回值,每个类都有,自己不定义则系统给一个默认的,自己定义了系统就不给默认的。默认的形如: 类名(){}
- 可以重载
- 常值数据成员、对象成员、从基类继承来的数据成员等特殊成员的初始化必须采用初始化表。
- 单参构造函数可完成类型转换功能。
- 构造函数的调用顺序:
- 规则一:对象的创建按定义的先后顺序
- 规则二:含对象成员的类,先对象成员(多个时按定义顺序)后类自己
- 规则三:先基类再派生类,有多个基类时按派生顺序。
- 规则四:有虚基类时,按虚基类、基类、派生类的顺序
- 复制构造函数(拷贝构造函数):
在以下情况被调用
-
- 一个对象以值传的方式传入函数体
- 一个对象以值传的方式传出函数体(return)
- 一个对象需通过另一对象进行初始化
(4)析构函数:
- 对象生存期结束时由系统自动调用,形如: ~类名(){……}
- 无返回值,每个类都有,不可以重载,可以定义为虚函数
- 析构函数的调用顺序:与构造函数相反
(5)内联成员函数:默认在类定义中实现的成员函数为内联的,要在类外对成员函数进行实现,并定义为内联的函数,需加关键字inline
(6)友元:分为友元函数和友元类,以破坏类的封装性来换取访问的效率。
友元函数:不是类的成员函数,加friend关键字,不带this指针,但可以直接访问类中的所有成员。
(7)静态成员:静态数据成员和静态成员函数,没有this指针
- 静态数据成员:所有的类对象共享静态数据成员
- 静态成员函数:只能直接访问该类的静态数据成员,不能直接访问非静态数据成员。
(8)const对象和const成员函数:
- const对象必须初始化,且不能被更新,只能调用const成员函数。
- const成员函数不能直接修改对象的数据成员值,也不能调用其他成员函数间接修改对象的数据成员值。
- const关键字可以被用于作函数重载的区分。
(9)运算符重载
- 可以用不同的方式进行重载:友元函数,成员函数,普通函数
- 运算符重载为普通函数的方式和重载为友元函数的方式类似,但要注意类中数据成员的访问权限。
- 双目运算符重载成友元函数,需要两个形参,第一个对应左操作数,第二个对应右操作数;重载成成员函数,需要一个形参,当前对象为左操作数,形参为右操作数
- 单目运算符重载成友元函数,需要一个形参;重载成成员函数,不需形参
- 要求掌握能重载的运算符有++、--、+、-、*、/、+=、-=、*=、/=、<<(插入运算符)、>>(提取运算符)
- 重载++、--时注意如何区分前置和后置。
- 运算符重载的规则:
- ?: . .* :: sizeof 不允许重载
- = -> () [] 只能重载成成员函数
- >> << 只能重载成友元函数
- 运算符重载不能改变运算符的操作数个数、优先级别、结合性
(10)继承和派生
- 单一继承、多重继承
- 公有派生、私有派生、保护派生
(11)抽象类:不能创建对象,但可以作为基类
- 构造或析构函数为protected的类
- 含有纯虚函数的类
(12)优先规则(支配规则)和赋值兼容性
- 优先规则(支配规则):派生类新增成员名与基类成员名相同时,若未用类名限定,派生类定义的成员名优于基类中的同名成员
- 赋值兼容性:
- 派生类对象可以赋给基类对象,反之不可
- 可将派生类对象的指针赋给基类型的指针变量
- 派生类对象可以初始化基类型的引用
- 通过基类指针(引用)只能访问从相应基类中继承来的成员,不允许访问派生类中的新增成员。
(13)虚基类:注意虚基类构造函数的调用
(14)多态性:编译时多态性(函数重载或运算符重载)和运行时多态性(虚函数—类中的非静态成员函数可定义为虚函数)
运行时的多态性触发的条件:基类指针(引用)指向派生类,并通过基类指针(引用)调用虚函数,触发多态性。
补充:
1.C++程序从上机到得到结果的几个操作步骤依次是编译、运行、编辑、连接。
2.能作为C++程序的基本单位是函数。
3.在C++语言中,int型数据在内存中的存储形式是补码。