一、C++的特点与基本格式
1.特点
① C++是一种“更好的C语言”(对C语言的类型进行了改革和扩充)
② C++与C完全兼容
③ 支持面向对象程序设计
④ 生成的代码质量高
2.基本格式
① 编译预处理命令 #include<iostream> (输入输出流)
② 命名空间 using namespace std;
③ cin>>用于输入,cout<<用于输出,endl用于换行
④ main函数的返回值为整型,即int main( )
⑤ 源文件扩展名.cpp
目标代码文件(编译后)扩展名.obj
可执行文件(连接后).exe
二、简单的程序设计
2.1 字符集、标识符和关键字
1.C++的字符集:
大小写英文字母a~z、A~Z
数字字符0~9
特殊字符:空格 ! # % ^ & *等
2.C++的标识符:用于命名变量、函数、数组及符号常量等(起名字)
标识符的定义遵循如下规则:
①标识符由大小写英文字母、数字符号和下划线组成,且第一个字符必须是字母或下划线,例如0_0就是非法字符
②字母大小写是有区别的
③标识符不能与关键字相同
2.2 数据类型
(1)基本数据类型:整型、浮点型、字符型、布尔型、void型
(2)构造数据类型:数组类型、指针类型、枚举类型、结构体类型、共用体类型
(3)类
2.3 常量与变量
2.3.1 常量
① 整型常量(十进制、八进制、十六进制)
② 实型常量(小数形式、指数形式形如1.5e2)
③ 字符型常量:
普通字符:用单引号括起来的一个字符,例如’A’,在内存中占用一个字节,其值为对应的ASCII码值
转义字符:以\开头的字符序列,用来表示控制字符、字母字符、图形字符和专用字符,例如\0表示字符串的结束标志
④ 字符串常量:用双引号括起来的字符序列,以\0作为结束标志
例如”hello”在内存中存放为h e l l o \0,即长度为6个字节
⑤ 布尔型常量: true表示真,false表示假
⑥ 符号常量:用”#define 标识符 常量”来定义一个符号常量,增加程序可读性
例如”#define PI 3.14159”
2.3.2 变量
(1)变量的命名:采用标识符的命名规则,且原则是“见名知意”,例如_sum、num等
变量名不能与关键字、函数名和类名相同
(2)变量的声明格式:
类型标识符 变量1,变量2,…,变量n;
(3)变量的初始化:
例如 int a=3; float b(4.2);
(4)常变量:
定义格式: const 数据类型 标识符=初始值; 或 数据类型 const 标识符=初始值;
定义常变量时,必须同时初始化,此后它的值不能再改变
例如
const double PI = 3.14159;
注意:用#define定义的符号常量和用const定义的常变量是不同的!
2.4 不同类型数据的转换
① 隐式类型转换:
当表达式中操作数据的类型不同时,编译系统会自动按照数据类型精度级别的高低,将其转换为同一类型
② 强制类型转换:
它把表达式值的类型强制转换成指定的类型
格式: (类型)表达式; 或 类型(表达式);
例如(double)3/2的意思就是先把3强制转换成双精度类型,再把2隐式转换成双精度类型,最终得到的值是双精度数1.5
注意:强制类型转换后得到的是一个所需类型的中间变量,原本变量的类型没有发生任何变化。
2.5 运算符与表达式
① 算术运算符(+ - * / % ++ --)
由算数运算符、操作数和括号构成的表达式称为算术表达式
注意:
++ -- 为单目运算符,即只有一个操作数,设操作数为i,则:
++i 表示先将i的值自增1,然后用i的值;
i++ 表示先用i的值,然后再将i的值自增1;
--i 表示先将i的值自减1,然后用i的值;
i-- 表示先用i的值,然后再将i的值自减1;
② 赋值运算符(=)
变量=表达式;
将右边的表达式的值赋给其左边的变量,赋值运算的结合性为自右向左
复合赋值运算符:+= -= *= /= %=等
③ 关系运算符(== > < >= <= !=)
运算的结果为true(1)或false(0),结合性是自左向右
④ 逻辑运算符(! && ||)
其中!是单目运算符,其意义是“非”,例如a是true,则!a表示“非a”,即false
&&表示“与”,||表示“或”,都是双目运算符
逻辑运算符的结合性为自左向右,优先级由高到低为! && ||
例如,判断year是否闰年的条件用逻辑表达式可表示如下:
(year%4==0)&&(year%100!=0)||year%400==0;
⑤ 逗号运算符(顺序求值)
例如 a=3*5,a*4; 则先计算a=3*5,然后计算a*4,得到表达式的值为60
⑥ *位运算符
⑦ 条件运算符
(表达式1)?(表达式2):(表达式3)
这是唯一的一个三元运算符,执行规则为:
先计算表达式1的值,然后判断表达式1的值,若表达式1的值为非0,则整个表达式的值为表达式2的值;若表达式1的值为0,则整个表达式的值为表达式3的值
可以替换if-else语句
2.6 基本控制结构及语句
C++基本控制结构:顺序、选择和循环结构
C++语句可分为5类:表达式语句、函数调用语句、复合语句、控制语句和空语句
主要学习的控制语句有:
① if…else
② switch
③ do while
④ while
⑤ for
⑥ break
⑦ continue
⑧ return
⑨ goto
1.if...else语句
if(表达式)
语句1;
else
语句2;
意思是判断if中的表达式是true还是false,如果是true,就执行语句1,如果是false,就执行语句2
例如判断year是否为闰年:
if(year%4==0&&year%100!=0||year%400==0)
cout<<year<<“是闰年”<<endl;
else
cout<<year<<“不是闰年”<<endl;
注意:在if语句嵌套时,else总是与它上面最接近的且未曾匹配的if配对
2.switch语句
switch(表达式)
{ case常量表达式1:
语句序列1; break;
case常量表达式2:
语句序列2; break;
…
case常量表达式n:
语句序列n; break;
default:
语句序列n+1;
}
注意:
① switch括号中的表达式只限于整型、字符型或枚举型表达式
② 要使流程跳出switch语句,可用一个break语句来实现
③ 如果没有相匹配的常量表达式,就执行default后面的语句
④ default可以没有,但至多出现一次,通常将它写在全部情况之后
3.while语句
while(表达式)
语句(循环体);
计算表达式的值,当表达式为true或非0时,执行循环体,然后重新回到计算表达式的值……
当表达式为false或0时,结束while语句
4.do-while
do{
语句(循环体);
}while(表达式);
与while语句基本相同,但要注意,do…while语句至少执行一次循环体
5.for语句
for(表达式1;表达式2;表达式3;)
{
语句;
}
其中表达式1是循环变量赋初值,表达式2是循环条件,表达式3是循环变量增值,语句为循环体
注意:
① 三种循环语句是可以相互转换的
② 循环是可以嵌套的
其他辅助控制语句:
break、continue、return、goto
①break语句用于循环的提前中止。当循环体中的break语句被执行时,程序流程会立即跳出所在的循环(循环嵌套时只跳出所在层的循环),执行下一条语句。
break语句只能用于循环语句和switch语句。
②continue语句只用在循环语句中。其作用是跳过循环体中剩余的语句,结束本次循环,但并没有中止循环,循环依然进行下去。
continue语句常常与if语句一起使用,用于加速循环
③return语句结束当前正在执行的函数,并将控制权返回给调用此函数的函数
④goto语句(无条件转移语句)只能在函数体内跳转,不能跳到函数体外的函数
例子:
使用循环嵌套结构输出九九乘法表
#include <iostream>
using namespace std;
int main() {
for(int i = 1; i <= 9; i++) {
for(int j = 1; j <= i; j++) {
cout << j << " x " << i << " = " << i*j << "\t";
}
cout << endl;
}
return 0;
}
三、构造数据类型
3.1 数组
数组:具有相同类型的若干变量按序排列的数据元素的集合。
1.一维数组
其一般格式为: 类型名 数组名[整型常量表达式]
例如 int a[10];
注意:int a[10];代表a中有10个元素,但是其中第一个元素为a[0],最后一个元素为a[9],使用a[10]会导致越界。
一维数组的初始化方法:
全部初始化: int a[5]={66,80,75,92,55}; 或 int a[]={66,80,75,92,55};
部分初始化: int a[5]={66,80,75}; (缺位用0补充)
2.二维数组
其一般格式为:
类型名 数组名[整型常量表达式1] [整型常量表达式2]
例如 int a[2][3];
其存储方法为先行后列,即先存放第一行的3个元素a[0][0]、a[0][1]、a[0][2],再存放第二行的3个元素a[1][0]、a[1][1]、a[1][2]
全部初始化方法有如下三种:
①int a[2][3]={{1,2,3},{4,5,6}}
②int a[2][3]={1,2,3,4,5,6}
③int a[][3]={1,2,3,4,5,6}
部分初始化:
int a[2][3]={{1},{6,7}} (缺位用0补充)
3.2 指针
3.2.1指针
指针:指针变量是能存储和操作地址的变量
指针变量的定义形式为: 类型名 *指针变量名
例如 int *p;
注意:任何指针变量所占用的存储空间是相同的,都是long int型数据
有以下两种运算符:
*代表取内容 &代表取地址
这两种运算符是互逆的
也就是说:p=&i;的意思就是把i的地址赋给p
完整语句为:int i,*p=&i;
如此一来,*p的值就为i了,p的含义为i的地址
cout<<i;与cout<<*p;的效果完全相同,只不过前者是直接访问,后者是间接访问。
3.2.2指针赋值
int x=1,*p1=&x; int y=2,*p2=&y;
执行“p1=p2”后,指针p2指向x,*p2相当于x;执行”p2=p1”后,指针p1指向y,*p1相当于y
注意:二指针必须是同一类型的,如int和float之间就无法赋值
指针与数组的关系:
例:int a[6]={1,2,3,4,5,6};
根据前面的知识,可知a[0]=1,a[1]=2,a[2]=3,a[3]=4,a[4]=5,a[5]=6
让指针p指向a[2]: p=&a[2]; 可知*p此时相当于3
要点:
指针p+2指向a[4],*(p+2)的值为5,p+2表示从当前位置向后移动两个数据位置。
一般地:p+n表示指针从当前位置向后移动n个数据位置,指针移动的字节数=n*sizeof(所指向的数据类型)
指针同样可以进行++p、p++、--p、p--操作
*指针数组:所有元素都是指针类型变量的数组
主要用于处理多个字符串的场合
3.2.3动态内存分配
动态内存分配需要做三项工作:
①定义指针变量
②动态申请空间——new运算符
③动态回收空间——delete运算符
其中①②可合并
1.动态申请空间——new运算符
格式为:new 类型名;
其作用是申请一片内存空间来存放该类型的数据
例:申请单个变量 int *p=new int(5);
申请一片连续的空间——动态数组
int *p=new int[N] //动态申请有N个int型元素的数组
2.动态回收空间——delete运算符
格式为:delete 类型名;
其作用是释放new运算符申请的内存空间,一定与new一起使用
例:释放单个动态变量 delete p;
释放动态数组变量 delete []p;
3.2.4指针辨析
*常指针:
int * const p=&x; 则p为常指针,其固定指向变量x,不能让其指向其他变量,但p指向变量的内容是可以改变的。
*指向常量的指针:
int const *p=&x; 则p为指向常量的指针,p指向变量的内容不可改变,但p指向的变量可以改变。
*指向常量的常指针:
int const * const p=&x; 则p为指向常量的常指针,p指向的变量和内容都不能改变。
3.3 引用
格式: 类型名 &别名=目标变量名;
作用为给目标变量起一个别名
例如:
int x=1;
int &y=x;
则此时y为x的别名,二者无区别,输出y的值与输出x的值均为1
注意:引用的&在等号左边,指针的&在等号右边,因此可用这种方式来区分引用和指针
注意:
①目标变量名的类型必须与别名类型一致
②引用必须是变量的别名,表达式和常量不能被引用
③定义引用时,必须就地初始化,即int &x=y;
④引用不会占用内存空间(占用同一块存储空间)
⑤若定义x为y的别名,则x与y绑定,x不能作为其他变量的别名
⑥定义数组a(a有n个元素)的引用方式为:
int (&m)[n]=a; 此后,数组m与数组a等价
引用与指针的区别:
①引用比指针简单,容易理解,在二者都能达到目的时,优先使用引用
②指针可以是空指针,引用不可为空
③指针可以从指向一个变量,转到指向另一个变量;引用定义为某个变量的别名,就一直是该变量的别名,不能成为其他变量的别名。
3.4 字符串
3.4.1字符数组
C风格处理字符串方式:字符数组
可以用字符数组来处理字符串
一般格式:char 字符数组名[整型常量表达式]
例如: char a[10];
初始化方式:
逐一赋值:char a[5]={‘C’,’+’,’+’};
字符串赋值:char a[5]={“C++”};
注意:用字符串赋值时,需要多占用一个字节存放字符串结束符’\0’
因此,上述字符串赋值中的字符串占用4字节(可用sizeof显示)
输出方式:
①通过for语句逐个字符输出
②利用cin或cout将字符串一次性输入或输出
cin>>字符数组名;
cout<<字符数组名;
③通过函数getline输入一整行内容
cin.getline(字符数组名,数组长度,结束标记);
3.4.2字符指针
C风格处理字符串方式:字符指针
还可以用字符指针来处理字符串:
一般形式:char *p;
指针指向字符串的3种方法:
①将字符指针指向一个字符串常量
char *p=”wo shi hao ren”;
②将字符指针指向一个字符数组
char s[14]={“wo shi hao ren”};
char *p=s;
③将字符指针指向动态申请的字符数组
char *p=new char[20];
字符数组与字符指针的比较
①字符数组存储着字符串的全部字符;字符指针存储的是指向存储字符串的区域的首地址
②初始化以及赋值方式不同:字符数组必须在定义时整体赋值(在一行语句内);字符指针可以在定义时初始化,也可以先定义,后赋值。
③指针必须有确定的指向(指针初始化)
④指针的值可以改变,数组名作为指针常量值不能改变。
3.4.3string类
使用时先要在程序开头加上#include<string>
3.5 *枚举变量、结构体与*共用体
结构体:
一般格式: struct 结构体名{结构成员描述};
例如:struct student
{
char number[20];
char name[20];
char tel[20];
int age;
};
注意最后的分号!!!
四、函数
4.1 函数的定义和调用
大部分的函数有返回值,其一般形式如下:
函数类型 函数名(参数表)
{ 声明部分;
语句部分;
return 表达式;}
例如:
int min(int x,int y);
{
int z;
z=x<y?x:y;
return z;
}
这个函数可以实现求两个整数中较小的一个
注意:
①函数类型是指函数返回值的类型
②函数名必须合法,最好有实际意义
③形参部分的格式如下:
类型1 变量名1,类型2 变量名 2,……,类型n 变量名n
若函数不需要参数,形参可以不写或写void
④return 表达式;可以让函数计算结果返回给调用它的程序
4.2 函数的参数传递
4.2.1值传递
值传递是单向参数传递,实参值可以传给形参,但形参值不会传给实参。
4.2.2引用传递
在函数定义时,将形参声明为实参的引用,也就是实参的一个别名
如此一来,实参和形参占用同一内存空间,实参值和形参值可以相互传递
4.2.3地址传递
实参的地址传递给对应的形参(指针变量或数组名等)
通过被调函数对形参的间接运算,从而改变实参的值
4.3 递归函数、嵌套调用
4.3.1函数的嵌套调用
C++不允许对函数作嵌套定义,即在一个函数中不能包含另一个函数的定义 在一个程序中每一个函数的定义都是互相平行和独立的
C++可以嵌套调用函数,即被调用的函数又调用另一个函数
例如,main函数调用函数A,而函数A又调用函数B,函数B又调用函数C,这样函数main间接调用了函数B和C,从而形成了函数嵌套调用的一条链子:main--A--B--C。
4.3.2递归函数
所谓递归函数,就是在自己的函数体中调用自己的函数,一般用于内容重复出现的情况,例如阶乘、汉诺塔
例子:斐波那契数列
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll feibo(int n) {
if (n < 2) return n;
return feibo(n - 1) + feibo(n - 2);
}
int main()
{
ll n;
cin >> n;
cout << feibo(n) << endl;
system("pause");
return 0;
}
4.4 内联函数
内联函数能够做到直接用实参替代形参,不涉及参数传递,以达到节省运算时间的目的,但重复使用却大幅增加了空间,一般不使用,记住其关键词即可:inline
注意:
内联函数是以空间换时间,即节省运行时间,但却增加目标代码长度
通常,将规模很小(一般为5个语句以下)而使用频繁的函数(如定时采集数据的函数)声明为内联函数
内联函数中不能包括复杂的控制语句,比如,循环语句和switch语句
将函数声明为inline函数,只是对编译系统的建议,并非是强制的,编译系统会根据具体情况决定是否将函数作为内联函数处理,如果一个函数的规模大,或者包含循环语句,那么即使写上inline关键字,编译系统仍将它视为普通函数
4.5 带有默认值的形参
函数形参允许带有默认值default,即在形参部分给变量赋值。
4.6 函数重载
函数重载就是允许让功能类似的函数使用同一函数名,以增加程序的可读性
例如若想要编写一个计算矩形(边长为a和b)、圆形(半径为r)面积的函数,可使用函数重载
double area(double a,double b);
double area(double r);
此时两个不同的函数使用了同一名字area,系统会在调用过程中自动匹配想要的那个
但请注意:如果函数重载和形参默认值同时出现,可能会引发歧义。
例如:
double area(double a=2,double b=3);
double area(double r);
如果调用函数area(2),系统会分辨不清你需要的是area(2,3)还是area(2)
4.7 系统函数
系统函数:C++已有的常用函数,不需要人为定义,可直接调用,如sqrt(int)
使用系统函数前,必须用include指令将相关的头文件嵌入到程序中
4.8 变量的作用域与生存期
4.8.1作用域
作用域可分为函数原型作用域、块作用域、类作用域和文件作用域
函数原型作用域是指函数原型声明中的形参的作用域,例如int cnm(int s,int b);
这里s和b的作用域仅限于形参表内,其他地方都不能使用。
块作用域最为常见,例如for(int i=0,i<10,i++) ,这个i的作用域是这个for循环,在别的地方无法使用这个i,如果i被广泛使用,建议将其在for循环前就定义。
可见域非常容易理解,指的是变量实际生效的区域,可见域是作用域的子集
例如 int py=1;
int main()
{ int py=2;
…… }
上面的程序中,第一个py的作用域是从定义到程序结束,其在主函数外,故被称为全局变量,第二个py的作用域也是从定义到程序结束,但是局部变量,有区别的是两个py的可见域不同,第一个py的可见域仅仅是前两行(第三行被新的py覆盖),第二个py的可见域是从定义到程序结束。
4.8.2生存期
变量的生存期是指一个变量在程序运行过程中实际占用内存或寄存器的时间。
变量的存储类型可分为四种:自动auto、寄存器register、外部extern、静态static
auto可以将局部变量放置于动态存储区,一般变量被定义时都是默认auto
register可以将局部变量放置于寄存器中
extern可以将全局变量变为外部变量
static可以将变量变为静态变量,可以让一个局部变量有全局寿命(作用域不变),只进行一次初始化
五、类与对象
5.1 类与对象概述
类由说明部分和实现部分组成
说明部分的形式如下:
class 类名
{
private:
成员表1;
protected:
成员表2;
public:
成员表3;
};
实现部分的形式如下:
类名::成员函数名(参数表)
{函数体;}
注意,在类体内不能对数据成员进行初始化
同时,private,protected,public三个关键字对数据成员有不同的访问控制
private可以让数据成员变为私有成员,这些成员只能在类内使用,如果在类内没有写三个关键字的任意一个,则数据成员默认为私有成员;
public可以让全数据成员变为公有成员,全部函数都能存取共有成员的数据,其定义了类的外部接口;
protected可以让数据成员变为保护成员,只有该类内的函数、该类的派生类内的函数才能存取保护成员的数据。
5.2 构造函数与析构函数
构造函数的功能是将对象初始化,其特点是与类同名,且无返回类型
析构函数的作用是在对象的生存期即将结束时,释放对象所占有的内存空间,其特点为无返回类型、不接受任何函数、一个类只能有一个、释放对象内存空间顺序与定义对象顺序相反。
5.3 对象数组和对象指针
对象数组中的每一个元素都是同一个类的对象,因此对象数组调用函数时,有几个对象就调用几次
定义一个一维对象数组的格式为:类名 数组名[下标表达式];
使用对象数组成员的一般格式为:数组名[下标].成员名;
对象指针,它用于存放对象的地址,声明对象指针的一般格式为:
类名 *对象指针名;
例如
Clock P,*p=&P; //将对象k的地址赋值给p,使p指向P
通过对象指针访问对象成员的方法为:
对象指针名->成员名;
(*对象指针名).成员名;
这些前面都提到过,要说明的是声明对象指针时,构造函数不被调用,因为指针的特性为其指向的数据类型为长整型,与类不相符,因此不被调用。
5.4 静态数据成员与静态成员函数
使用关键字static可以让变量或函数变为“静态”,从而实现数据共享。同理在类中,也有静态数据成员和静态成员函数,它们实现了对象之间的数据共享。
定义静态数据成员时只需在前面加上static关键字即可,访问它的方法为:
类名::静态成员标类;
静态数据成员常常用来担任多个对象之间的联结,例如要计算两个对象调用函数的次数,只需定义一个静态数据成员count,这样就能统计次数了。
注意:
静态数据成员必须在类歪初初始化,在初始化时,需用类名限定:
数据类型 类名::静态成员=0;
5.5 友元函数
友元函数是一种特殊的函数,它可以访问封装于一个类中的任意数据(包括私有成员),以达到数据共享的目的,但会破坏类的封装性和隐蔽性。
声明一个类的友元函数的格式如下:
friend 数据类型 函数名(参数表); //这步需在想要成为友元的类中执行
注意,一个类的友元函数不是该类的成员函数,只是要在类中声明。
六、继承与派生
6.1 派生类
所谓派生类,就是指一个类继承了其基类的部分特征,同时又有自己的特性。这样能够支持代码重用,建设冗余数据的存储,提高程序开发效率。
定义派生类的格式如下:
class 派生类名 : 继承方式 基类名
{ private:
派生类新增的私有数据成员和函数成员的描述语句;
public:
派生类新增的公有数据成员和函数成员的描述语句;
protected:
派生类新增的保护数据成员和函数成员的描述语句;
};
其中,继承方式以及基类访问控制属性不同,派生类成员及其对象访问的权限也不同。
具体参见下表:
可见,只有当继承方式和基类中访问控制属性都为public时,派生类对象才可访问基类成员。
6.2 派生类的构造函数和析构函数
派生类与普通类一样,都有各自的构造函数和析构函数,构造函数用来初始化,析构函数用来释放对象占用的内存空间,但与普通类不同的是,派生类的两种函数的格式与普通类有差别。
派生类的构造函数:
① 最简单的派生类的构造函数:
派生类构造函数名(总参数列表,即它自己的参数加它基类的参数):基类构造函数名(基类的参数列表)
{ 派生类中新增数据成员初始化语句; }
②有内嵌对象的派生类的构造函数:
派生类构造函数名(总参数列表):基类构造函数名(基类的参数列表),内嵌对象名(内嵌对象参数)
{ 派生类中新增数据成员初始化语句; }
特别要注意的是,此构造函数的执行顺序为首先基类,然后内嵌对象,最后派生类自身!
③多层派生的派生类的构造函数:
格式与一般构造函数相同,但是要注意的是,由于参数传递,每一个派生类只负责给它的直接基类准备参数,例如B是A的派生类,C是B的派生类,那么C的构造函数只需准备B和自己的参数即可。
此构造函数的执行顺序为从最远的基类到它自身。
派生类的析构函数的定义方法与一般(无继承关系时)类的析构函数相同。
派生类的析构函数会自动隐式调用,因此若题目无特殊要求,一般不用管析构函数。
析构函数与构造函数的调用顺序是相反的。例如在有内嵌对象的情况中,先执行派生类自身的析构函数,再执行内嵌对象的,最后执行基类的。
七、多态性
7.1 运算符重载
许多运算符(例如+、-等)只能用于变量之间的运算,而当我们想要进行对象之间的运算时,不能直接使用这些运算符(例如c3=c1+c2),而是要通过调用成员函数或是友元函数的方式来间接进行对象中的数据成员的运算。
那么,运算符重载(重载多态)就是一种新的方法,我们可以把一个运算符重载为成员函数或是友元函数,使其拥有成员函数或友元函数的功能,这样就能使程序更加直观、简洁。
例如:
为了计算两个整数的和,一般情况下,我们需要定义一个函数add,调用时写c3=add(c1,c2),这样就能完成加法的操作,这是比较直观的情况。
如果是复数相加呢?
调用时则要写c3=c1.complex_add(c2),这个函数并不直观表明“c3=c1+c2”
使用运算符重载后,我们把“+”赋予add函数的功能,我们在使用时就能直接写“c3=c1+c2”了。
关于运算符重载,有如下规则:
①只能对已有的C++运算符进行重载,而不能臆造新的运算符
②”.”、”. *”、”::”、”?:”、”sizeof”不能被重载
③运算符重载后,操作数的个数不变、运算符的优先级不变、运算符的结合性不变、运算符的语法结构不变
④运算符重载后参数表不能有默认值
⑤有些运算符(例如=、[ ]、( )、->等)只能重载为成员函数;有些运算符(例如流插入运算符”<<”和流提取运算符”>>”)只能重载为非成员函数。
7.2 虚函数
根据之前提到的赋值兼容,可以用指向基类对象的指针去指向派生类的对象,也可以用派生类的对象初始化基类对象的引用。
有如下情况:基类和派生类有相同的成员函数,我们定义一个指向基类对象的指针,如果它指向基类对象就调用基类的成员函数,如果它指向派生类对象就调用派生类的成员函数。这看似可行,但事实上这个指针无论指向哪个类的对象,最终都会调用基类的成员函数。
为了让我们能够在使用指向基类对象的指针或引用的情况下调用派生类的同名成员函数,我们可以使用虚函数(包含多态),其一般形式为:
virtual 函数返回值类型 虚函数名(形参表){函数体}
八、输入输出流
8.1 输入输出重定向
C++的流类库
四个标准的输入输出流对象:
cin(console input的缩写):输入流类的对象
格式:cin >> x; 其中,运算符">>"重载,表示从流里提取数据给变量x,
所以">>"称为“提取”运算符
cout(console output的缩写):输出流类的对象
格式:cout << 表达式; 其中,运算符"<<"重载,表示变量x向流里插入数据,所以"<<"叫“插入”运算符
另外还有两个输出类对象:cerr和clog,用于在屏幕上显示异常
和错误信息.
8.2 文件操作
如果在程序中要使用文件,必须在程序开头增加包含语句:
#include<fstream>
九、模板
9.1 函数模板
模板即第三种多态:参数多态
定义模板的一般形式为:
template<类型参数表>
返回值类型 函数名(函数参数表)
{函数体}
其中:
template是定义模板的关键字
在类型参数表中,写<typename T1,typename T2…>
模板的特点:
将数据类型作为参数,具有相同处理方法、但不同类型的数据对象
9.2 类模板
类模板定义的一般形式:
template<模板参数表>
class 类模板名
{ 类成员的声明; };
在类模板外定义其成员函数的方法:
template <模板参数表>
返回值类型 类模板名<模板参数顺序表>::成员函数名(函数参数表)
{函数体;}
类模板同函数模板,都需要在主函数中给出实际参数类型,才能变成一个真正的类
定义类模板的对象的方法:
类模板名<类模板实参表> 对象名(构造函数实参表);
显然比一般的类的对象多了“类模板实参表”
以Point类为例,想要的数据类型为整型,则可写成“Point<int> p1(1,2);”