C++
- 不要把面向对象和基于过程对立起来,面向对象和基于过程不是矛盾的,而是各有用途、互为补充的。
- 学习C++,既要学会利用C++进行基于过程的结构化程序设计,也要会利用C++进行面向对象的程序设计。
目录
C语言与C++之间的区别
C语言 | C++ |
---|---|
基于过程:程序=算法+数据结构 | 面向对象并基于过程 |
C语言要求变量的定义应该放在所有的执行语句之前 | C++只需要在第一次使用该变量之前进行定义即可 |
C语言中,输入和输出的功能是通过调用scanf函数和printf函数来实现的,输入和输出单个字符的函数:getchar和putchar | C++中是通过调用输入输出流库中的流对象cin和cout实现的,同时也依旧支持C语言中的输入输出 |
C语言没有提供逻辑型数据 | C++中增加了逻辑型数据,逻辑常量只有两个:false和true |
C语言不支持内联函数、函数的重载、函数模板和有默认参数的函数 | C++中支持内联函数、函数的重载、函数模板和有默认参数的函数 |
C语言使用字符数组来存放字符串。C-string方法 | C++中提供了一种新的数据类型,字符串类型(string类型),它是在C++标准库中声明的一个字符串类。string方法 |
C语言中不存在引用 | C++中存在引用 |
C语言中结构体成员只能是数据 | C++中结构体成员既可以包括数据,也可以包括函数 |
C语言中使用库函数malloc和free来分配和撤销内存空间 | C++中使用运算符new和delete,来进行,执行效率高 |
C语言中定义枚举类型变量时需要包括关键字enum | C++中可以不包括enum,直接用枚举类型名 |
C语言强制类型转换格式为(int)a | C++中强制类型转换格式为int(a) |
一、C++基于过程的程序设计
1、简单程序实例
example 1:
#include <iostream>
using namespace std;
int main()
{
int max(int x, int y);//函数的声明
int a,b,c;
cin>>a>>b;
c = max(a, b);
cout<<"max = "<<c<<endl;
return 0;
}
int max(int x, int y)
{
int z;
if(x > y) z = x;
else z = y;
return z;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
main
函数必须声明为int
型,因为操作系统要求执行一个程序后必须向操作系统返回一个数值:如果程序正常执行,则向操作系统返回数值0;否则返回数值-1;- 函数体是由”{}”括起来的;
cout
是C++系统定义的对象名,称为输出流对象;cin为输入流对象(输入时两个数据间用一个或多个空格间隔,不能以逗号或其他符号间隔);- “
#include < iostream >
” 是C++的一个预处理指令,是一个包含指令,作用是把文件”iostream”中的内容包含到该命令所在的程序文件中,代替该指令; - “
using namespace std
“的意思是使用命名空间”std”,C++标准库中的类和函数是在命名空间std中声明的; - “//”代表从它开始到本行末尾之间的全部内容都作为注释,同时对于多行注释也可以用”/ …… /”来表示;
- 程序第5行是对max函数的声明,声明过后max函数可以写在main函数后面;
2、C++程序的实现过程
- 每一个程序单位由以下三部分组成:预处理指令、全局声明、函数
3、变量
3.1 变量基础
- 变量属于标识符。标识符指用来标识变量、函数、数组、类型等实体名字的有效字符序列;
- 变量名代表内存中的一个存储单元。当程序从变量中取值时,实际是通过变量名找到相应的内存单元,从其中读取数据;
- 变量具有两种属性:作用域(全局变量或局部变量)、存储期(静态存储期或动态存储期)
- 变量的命名规则:
- 只能由字母、数字、下划线3种字符组成;
- 第一个字符必须为字母或者下划线;
- 常变量又称为只读变量:
- 相当于在此变量的基础上加上一个限定:存储单元中的值不允许变化;
- 区别使用
#define
指令定义的符号常量和用const
定义的常变量:
符号常量 | 常变量 |
---|---|
符号常量只是用一个符号代替一个字符串,在预编译时把所有符号常量替换为所指定的字符串,它没有类型,在内存存在以符号常量命名的存储单元。 | 常变量具有变量的特征,具有类型,在内存中存在以它命名的存储单元,可以用sizeof运算符测出其长度。与一般变量不同的是指定变量的值不能改变。 |
3.2 局部变量与全局变量
- 局部变量
- 在一个函数内部定义的变量,它只在本函数或复合语句范围内有效
- 形参也是局部变量
- 在函数原型声明中出现的参数名,只在原型声明中的括号范围内有效
- 全局变量
- 在函数外定义的变量是外部变量,称为全局变量,其有效范围为从定义变量的位置开始到本源文件结束
- 如果在一个函数中改变了全局变量的值,就能影响到其他函数,使其他函数中引用的同名变量也同时改变
- 建议不在必要时不要使用全局变量(占用存储单元、函数通用性降低)
3.3 变量的存储类别
- 静态存储方式
- 在程序运行期间,系统对变量分配固定的存储空间
- 静态存储区中存放全局变量
- 动态存储方式
- 在程序运行期间,系统对变量动态地分配存储空间
- 动态存储区中存放:函数形式参数、函数中定义的变量(未加
static
的局部变量)、函数调用时的现场保护和返回地址等
- 变量的存储类别
- 指数据在内存中的存储方式
- 包括自动的(
auto
)、静态的(static
)、寄存器的(register
)、外部的(extern
)
- 自动变量:函数中的局部变量,如果不加
static
声明,编译系统动态分配存储空间 - 静态局部变量:函数中的局部变量的值在调用结束后不消失而保留原值,即其占用的存储单元不释放,下次调用该函数时,该变量保留上一次调用结束时的值。
- 虽然静态局部变量在函数调用后仍然存在,但其他函数不能引用它,在其他函数中不可见
- 静态局部变量是在编译时赋初值的(默认为0或空字符),而自动变量赋初值是在函数调用时进行
- 寄存器变量:需要时直接从寄存器中取出参与运算,不必到内存中存取
- 全局变量或外部变量:在函数外部定义的,作用域为从函数定义处开始到本程序文件末尾
extern
只是对一个已定义的外部变量做声明,以拓展作用域- 当使用
static
在定义外部变量时进行声明,则此外部变量只限于本文件引用,而不能被其他文件引用(静态外部变量)
- 自动变量:函数中的局部变量,如果不加
3.4 变量的声明与定义
- 函数的声明是函数的原型,函数的定义时函数功能的确立
- 声明分为定义性声明(需要建立存储空间,eg:
int a;
)和引用性声明(不需要建立存储空间,eg:extern int a;
) - 广义来说声明包括定义,不过为了叙述方便,把建立存储空间的声明称为定义,而把不需要建立存储空间的声明称为声明
4、运算符
- 运算符种类
- 算数运算符:+、-、*、/、%、++、–
- 关系运算符:>、<、==、>=、<=、!=
- 逻辑运算符:&&、||、!
- 位运算符:<<(按位左移)、>>(按位右移)、&、|、^(按位异或)、~(按位取反)
- 赋值运算符:=及其拓展赋值运算符
- 条件运算符:?:
- 逗号运算符:,
- 指针运算符:*
- 引用运算符和地址运算符:&
- 求字节数运算符:sizeof
- 强制类型转换运算符
- 成员运算符:.
- 指向成员的运算符:->
- 下标运算符:[]
- 其他:函数调用运算符
- 运算符的优先级:!>算数运算符(带有”<”或”>”号的优先级大于”==”和”!=”)>关系运算符>&&和||>赋值运算符
- 进行运算时不同类型的数据需要转为同一类型,转换规则为如下所示。其中横向向左的箭头表示必定转换;纵向的箭头表示当运算对象为不同类型时的转换方向, 箭头的方向只表示数据类型级别的高低,由低向高转换。
A[char,short] ==> B(int)
B --> C[unsigned]
C --> D[long]
D --> E[double]
F[float] --> E
- 1
- 2
- 3
- 4
- 5
- 自增和自减运算符:++i(在使用i之前,先使i的值加1)、i++(在使用i之后,i的值加1)、–i(在使用i之前,先使i的值减1)、(在使用i之后,i的值减1)
- 赋值运算符:如果赋值运算符两侧类型不一致,但都是数值型或者字符型时,在赋值时自动进行类型转换
double
赋值给float
时,要注意数值范围不能溢出- 字符型赋值给整形时,将字符的ASCII码赋给整形
- 将
int
,short
或long
型数据赋给一个char
型变量,只将其低8位原封不动地送到char型变量(发生截断) - 不同类型整型数据间的赋值归根到底就是一条:按存储单元中的存储形式直接传送
- 条件运算符(唯一一个三目运算符):
- eg:
max = (a>b)? a: b;
- eg:
5 程序结构
5.1 选择结构
if()...else if()...else
- 多分支选择结构:
switch() {case x: ...;case y: ...;default :...}
- 各个
case
和default
出现的次序不影响执行结果 - 在执行
switch
语句时,根据switch表达式的值找到与之匹配的cas
e子句,就从此case
子句开始执行下去,不再进行判断,所以此时需要”break
“语句来达到跳出的目的 - 多个
case
可以共用一组执行语句
- 各个
5.2 循环结构
while(表达式)语句
do 表达式 while(表达式)(do-while语句)
for(表达式1;表达式2;表达式3)语句
- 可以只用”
break
“或”continue
”跳出循环或跳出本次循环
6、函数
6.1 函数的分类
- 用户使用的角度:
- 系统函数,即库函数
- 用户自己定义的函数
- 函数形式:
- 无参函数
- 有参函数
6.2 函数的定义
- 无参函数的定义
/*
类型名 函数名([void])
{
声明部分
执行语句
}
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 有参函数的定义
/*
类型名 函数名(形式参数列表)
{
声明部分
执行语句
}
*/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
6.3 函数的参数
形式参数 | 实际参数 |
---|---|
定义函数时函数名后面括号中的变量名称 | 在主调函数中调用一个函数时,函数名后面括号中的参数(可以为表达式) |
在未出现函数调用时,形参不占用内存中的存储单元 | 实参可以为常量、变量或表达式,但是要求变量必须有确定的值 |
实参与形参的类型应相同或赋值兼容(按照不同类型数值的赋值规则进行转换) | |
实参与形参变量的数据是值传递,即单向传递。只能由实参传给形参,无法传回 |
6.4 函数的调用
- 调用的方式
- 函数语句
- 函数表达式
- 函数参数
printstar(); //函数语句
c = 2 * max(a,b);//函数表达式
m = max(a, sqrt(b));//函数参数
- 1
- 2
- 3
- 函数调用时注意,当实参列表包括多个实参时,对实参的求值顺序并不是确定的。许多C++系统是按照从右至左的顺序求值。比如,若变量i的值为3时:
func(i, ++i);
/*func(3,4),从左至右顺序求值
func(4,4),从右至左顺序求值*/
- 1
- 2
- 3
6.4 函数的声明与函数原型
- 函数声明是指函数尚未定义的情况下,事先将函数的有关信息通知编译系统,以便编译可以正常进行。
- 函数声明中可以不写形参名,只写形参类型,这种声明成为函数原型
float add(float, float);//函数原型
/*函数类型 函数名(参数类型1, 参数类型2...);
函数类型 函数名(参数类型1 参数名1, 参数类型2 参数名2...);
- 1
- 2
- 3
函数的定义 | 函数的声明 |
---|---|
定义是指对函数功能的确立 | 声明是在定义前事先将函数的有关信息通知编译系统 |
包括指定函数名,函数类型、形参及其类型、函数体等 | 通知函数的名字、函数的类型以及形参的个数、类型和顺序(不包括函数体) |
6.5 内置函数(内联函数)
- 编译时将函数代码嵌套到主调函数中,而不是将流程转出去
- 可以在声明函数和定义函数同时写
inline
;也可以只在函数声明时加inline
,而定义时不加 - 内联函数中不能包括复杂的控制语句,如循环和
switch
语句
6.6 函数的重载
- C++允许同一个函数名定义多个函数,而这些函数的参数个数和参数类型可以不相同
- 不允许只有函数的类型不同而参数的个数和类型相同,如:
int f(int);
long f(int);
void f(int);
- 1
- 2
- 3
- 重载函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同,函数返回值类型可以相同,也可以不同
6.7 函数模板
- 建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表
- 定义函数模板的一般形式:
template <typename T>//T为类型参数;typename也可以用class代替,都表示类型名
T max(T a, T b, T c)
{
if(b>a) a = b;
if(c>a) a = c;
return a;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 只适用于函数体相同、函数参数个数相同而类型不同的情况
6.8 有默认参数的函数
- 指定默认值的参数必须防止形参列表的最右端
- 如果函数的定义在函数调用之前,则应在函数定义中给出默认值;如果函数的定义在函数调用之后,则在函数调用之前需要有函数声明,此时必须在函数声明中给出默认值,在函数定义时可以不给默认值
- 一个函数不能既作为重载函数,又作为有默认参数的函数
1
…
…
…
…
…
…
…
…
6.9 内部函数和外部函数
- 内部函数:只能被本文件中其他函数调用
static 类型标识符 函数名(形参表)
- 1
- 外部函数:可以供其他文件调用
extern int func(int a, int b)//省略extern,默认为外部函数
- 1
6.10 函数参数的传递
- 将变量名作为实参和形参:形参是变量的值,传递是单向的
void swap(int a, int b);
int i = 3, j = 5;
swap(i, j);//调用函数时,形参和实参不是同一个存储单元。值传递(传值方式)
- 1
- 2
- 3
- 传递变量的地址:形参是实参变量的指针变量(地址),实参是一个变量的地址
void swap(int *p1, int *p2);
int i = 3, j = 5;
swap(&i, &j);//值传递(传值方式),实参的值是变量的地址
- 1
- 2
- 3
- 形参为实参的引用:以引用作为形参,使形参名作为实参的引用
void swap(int &a, int &b);
int i = 3, j = 5;
swap(i, j);//地址传递传递(传址方式),实参是变量名,而传递的是变量的地址
- 1
- 2
- 3
7、数组
7.1数组的定义与引用
类型名 数组名[常量表达式]//一维数组的定义
数组名[下标]//一维数组的引用
类型名 数组名[常量表达式][常量表达式]//二维数组的定义
数组名[下标][下标]//二维数组的引用
- 1
- 2
- 3
- 4
- 二维数组元素排列顺序为:按行存放,即在内存中先顺序存放第1行元素,再存放第2行元素
- 多维数组元素在内存中的排列顺序:第一维的下标变化最慢,最右边的下标变化最快
7.2用数组做函数参数
- 数组元素可以做函数参数,用法与变量相同,传递数组元素的值;数组名也可以做实参和形参,传递的是数组的起始地址,C++实际上把形参数组名作为一个指针变量来处理
/*...*/
void select_sort(int array[], int n);//函数
int a[10];
select_sort(a,10);//调用函数
/*...*/
void select_sort(int array[], int n)//函数定义
{
/*...*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 在用变量做函数参数时,只能将实参变量的值传递给形参变量,在调用函数过程中如果改变了形参的值,对实参没有影响,即实参的值不因形参的值改变而改变
- 而数组名作函数实参时,如果改变了形参数组元素的值,将同时改变实参数组元素的值
7.3 字符数组
7.3.1 字符数组基础
- 只能对字符数组的元素赋值,而不能用赋值语句对整个数组赋值
char c[5];
c = {'C', 'h', 'i', 'n', 'a'};//错误
c[0] = 'C'; c[1] = 'h'; c[2] = 'i'; c[3] = 'n'; c[4] = 'a';//正确
char str[] = {"I am happy"};//正确,使用字符串常量初始化字符数组
char str[] = "I am happy";//正确
- 1
- 2
- 3
- 4
- 5
- 字符串结束标志:遇到字符
' '
表示字符串结束。对一个字符串常量,系统自动在所有字符后加' '
作为结束符。程序中检测字符串长度也依靠字符串结束标志 - 如果输入字符串长于字符数组的长度,此时系统并不报错,而是将多余的字符元素顺序地存放到所定义字符数组后面的几个字节中,有可能破坏其他数据,甚至出现无法估计的后果
7.3.2 字符串处理函数
- 字符串连接函数
strcat(char[], const char[]);
- 1
- 字符串复制函数
strcpy(char[], const char[]);
strcpy(str1, str2, 2);//将str2中前2个字符复制到str1中
str1 = str2;//错误,不能将字符数组直接赋值给另一个字符数组
- 1
- 2
- 3
- 字符串比较函数
strcmp(const char[], const char[]);
/*如果字符串1等于字符串2,函数值为0
如果字符串1大于字符串2,函数值为一正整数
如果字符串1小于字符串2,函数值为一负整数*/
- 1
- 2
- 3
- 4
- 字符串长度函数
strlen(const char[]);//其值为字符串实际长度,不包括' '
- 1
7.3.3 字符串类
- 将C++标准库中的string头文件包含进来
#include <string>
string string1;//定义字符串变量
string1 = "China";//赋值
- 1
- 2
- 3
- 可以使用简单的运算符实现连接(
+
)、比较(==,>,<,!=,>=,<=
)、复制(=
) - 字符串数组
- 不要求每个字符串元素具有相同的长度,同一个元素长度也可以变化
- 每个元素的值只包含字符串本身的字符,而不包括