当前位置:   article > 正文

C++程序设计基础_c++基础程序

c++基础程序

第一章 绪论

1972年设计C语言是为了写UNIX操作系统的,吸收了其他语言的优点逐步成为实用性很强的语言

优点:语言本身简洁,使用灵活方便;既有高级语言的特点,又具有汇编语言的特点、可移植性好

缺点:语法结构不够严密,程序设计的自由度大,对数据类型的检查机制弱,缺少支持代码重用的结构,难以适应开发特大型程序

C++:引入运算符的重载、引用、虚函数等功能

高级语言编译过程:

 尝试:输出hello world;输入输出变量

第二章 数据类型、运算符与表达式

常量与变量

常量:程序运行过程中保持不变的量,编译器根据表面形式判断类型

变量:在使用之前说明类型,在内存中占据与类型相应的存储单元

 变量命名方法:

字母、数字、下划线,不大于247个字符,大小写不通用

关键字:c++语法中默认的一些字int之类的

(一般使用匈牙利命名法命名)

整型数据:

默认十进制、八进制0开头,十六进制0x开头

整形变量分为有符号型与无符号型,short,int,long分别为2、4个字节

整型常量有长短之分(l、L);没有unsigned型,但是一个非负的整型常量可以赋给unsigned型的变量

实型数据:

实型数又称浮点数,两种表示方法:十进制或指数型(23e1,e后必须是整数)

单精度float,双精度double;分别占4、8个字节

实数=尾数*基数^阶码

定点数、浮点数都会出现溢出现象,当超出机器所能表示的最大值是,运算停止。

字符型数据(char):

作为整形数据在内存中存储,8位存储一个字符,用ascii码形式存储

‘’内括起来的字符表示该字符的ASCII码

因为内存中形式与整型数据相同,所以可以直接用整型值给变量赋值

 非打印字符:ASCII表示空格,回车13;转义字符\n \t \ddd \xhh \' \''

编译系统在见\时会接着找他后面的字符把他们处理成一个字符,在内存中占据一个字节

字符串常量 “”表示,在内存中顺序存放,以‘\0’结束(内存对应字符的ASCII形式)

 标识符常量 #define、const

变量:在程序执行过程中值可以改变的量。编译程序要为它分配若干字节连续的内存单元,保证变量的取值。变量说明在前,使用在后。

算数运算符和算术表达式

算数运算符+-*/%

注意:两个整数相除结果为整数;整数才可以求余数,符号和左边数的符号相同

优先级和结合性:()*/%+-

强制类型转化

(double)a bmw=2002转化原则:根据左边变量类型进行转化。

(1)unsigned转化后多余字节补0 (2)多子节变量转化后扩展最高位为1,符号不变

自增自减(难)

++a 先运算后赋值/先赋值后运算 c++

(1)只能用于变量,不能用于常量和表达式【表达式在内存中没有具体空间,常量所占空间不能重新赋值】

(2)结合方式从右到左

关系比较 >=!

结果来看:真为1,假为0  条件来看:所有非0值均为真

逻辑运算

&& || !(优先级从小到大:逗号、赋值、逻辑、关系、算术、!)

逗号运算符

顺序求解,结果为最后一个表达式的值,且优先级最低 a=3*3,a+6,a+7; 16 a=9

各类数值型数据之间可以混合运算,前提要先转化成同一类型数据

 第三章 简单的输入输出

输入 cin

C++所有输入输出都是通过输入输出流实现的

cin>>a>>b;输入语句会自动过滤空白字符,空格和回车是输入字符之间的分隔符

若要将每一个字符包括空格和回车赋给字符型变量时,使用cin.get()

若要输入16、8进制cin>>hex>>j; cin>>oct>>j; cin>>k;(仍为8进制,直到改变)

(1)只适用于整型变量

(2)所指明的数制保持有效,直到更新

(3)输入数据的格式个数类型要和定义变量一一对应。

输出cout

cout将双引号中字符串常量按照原样输出

若要指定输出项占用宽度:cout<<setw(6)<<i<<setw(10)<<j<<endl;

(1)包含在iomanip.h头文件中

(2)该设置仅仅对后面一个输出项有效

若想输出科学表示法:cout.setf(ios::scientific,ios::floatfield);//浮点数使用科学表示法输出

cout.setf(ios::fixed,ios::floatfield)//更新回到定点数表示形式

第四章 C++的流程控制语句

程序的三种基本结构:顺序、选择、循环 

嵌套条件语句(难点)

条件运算符

三目运算符:max=a>b?a:b

(1)条件运算符的优先级比赋值运算高

(2)结合方向从左到右

(3)三个表达式类型可以不同

switch语句 进入相应case语句执行后,遇到case和default不再进行判断,直到switch语句结束。如果要使其在执行完相应的语句后中止执行下一语句,可以在句尾+break。

switch和if:switch只能判断是否等于,if可以计算各种表达式。case子句后必须为常量,常常是整型和字符型。default可以省略。case和default可以颠倒。多个case语句可以共用一组程序。

while语句:一定有一个循环变量 (难点)注意判断完后还会++--

dowhile语句:首先执行一次循环体,在判断表达式。所以当第一次循环表达式的值为真时,while和dowhile的结果完全一样,否则结果不相同。

for语句:用得太多了

循环的嵌套

最大公约数与最小公倍数——欧几里得算法、打印位数、级数求和、求素数、打印图形

break在循环体中可以从循环体内跳出循环体,提前结束循环(一层)。

continue:结束本次循环,跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。

第五章 函数与编译预处理

函数是c++的基本模块。所编写的函数应尽量少与主调函数发生联系,便于移植。

1、一个源程序文件是由一个或多个函数组成,编译程序以文件而非函数为单位进行编译

2、一个函数可以有多个源文件组成,可以分别编译统一执行

3、cpp从main函数开始执行

4、函数可以嵌套调用,不可以嵌套定义

库函数是c++编译系统已经预定义的函数。

函数参数和函数的值

形参:被调函数中的变量(必须定义类型)

实参:主调函数赋给被调函数的特定值(一定是定值)。

形式参数和实际参数类型相同,一一对应

内存使用:1、在未出现函数调用时,形参不占用内存存储单元,只有开始调用时候才会分配空间

2、实参和形参单向传递,在内存中占据不同单元

3、可以在别的函数中使用相同的形式参数变量名

函数只能有唯一返回值,且返回值必须是函数的类型;还可以终止函数,并将控制返回到主调函数

函数应当出现在主函数之前(先定义,后调用原则),也可以使用函数原型声明,用来说明函数的返回值和形参的类型。

函数的嵌套调用

函数可以嵌套调用但是不可以嵌套定义,可以递归调用

作用域和存储类

作用域是指程序中所说明的标识符在哪一个区间内有效,即哪一个区间内可以使用或引用该标识符

C++五类标识符:

块作用域

花括号内的。一个函数内部定义的变量或一个块中定义的变量称为局部变量(形参也是局部变量

不同的函数可以使用相同的名字的局部变量,在内存中属于不同存储区见互不干扰(main函数中定义的变量也只在main中有效,属于局部变量)定义变量就是在内存中开辟空间。

变量名相同,局部更优先

文件作用域

在函数外定义的变量称全局变量,其作用域称为文件作用域,在整个文件中都可以访问

当全局变量与局部变量同名时,局部变量优先

块作用域中可以通过作用域运算符::来引用与局部变量同名的全局变量

函数原型作用域

从说明处开始到函数原型说明结束为止。

可以在函数原型声明中省略参量名称float tt(int,float);

存储类

生存期:动态存储变量/静态存储变量

静态存储:文件运行期间有固定的存储空间,直到文件运行结束

动态存储:程序运行期间根据需要分配空间,函数结束后立即释放空间 

局部变量分类

动态变量auto:默认存储在动态区

寄存器变量register:在cpu内部存储

静态局部变量:static:存储在静态区

动态局部变量未赋值时为随机值,作用域的函数或复合语句结束时空间被程序回收。

静态局部变量在静态区开辟存储空间,空间一直被保留直到程序运行结束,只能进行一次赋值。默认为0。

全局变量存储方式extern static

1、extern 全局变量more方式,当一个文件中要引用另一个文件中的全局变量或在全局变量定义之前要引用它时可以用extern说明,用来扩大全局变量的引用域。

2、static静态存储类别:仅能在本文件中饮用,即使其他地方用extern也不能引用。

当变量名相同导致作用域重合时,起作用的是最近说明的那个变量

内联函数

 

 内联函数实质是用存储空间(使用更多存储空间)来换取更少的执行时间,在函数定义时在类型之前增加inline以使用。

注意:C++中除了循环switch分支复杂嵌套if外,所有函数都可以定义成inline

缺省函数

C++中定义函数时,允许给参数指定一个缺省的值。调用时若明确给出了这种实参的值则使用实参,否则使用缺省的值。使用时需注意:

1、不可以靠左边缺省

2、函数原型说明时可以不加变量名

3、只能在前面定义一次缺省值

参数个数可变的函数

调用头文件“stdarg.h”可以使用库函数va_start(),va_arg(),va_end

va_list类型变量,与int,float类同,是c++系统预定义的一个数据类型(非float),只有通过这种类型的变量次啊能从实际参数表中取得可变有参量。va_list ap;

va_start(ap,b) 初始化 b为可变参数前最后一个确定的参数

va_arg(ap,int) 依次取参数 int:可变参数的数据类型名

int temp; temp=va_arg(ap,int);

va_end(ap)正确结束

函数的重载

不同功能的函数可以具有相同的函数名,C++根据函数的实参来确定应该调用哪一个函数

1、定义重载函数必须有不同的参数个数or参数类型

2、仅返回值不同时,不能定义为重载函数

编译预处理

宏定义

不带参数的宏定义,用一个指定的标识符代表一个字符串,凡是遇上就用字符串替代

标识符称为宏名,编译前的替代过程称为“宏展开”

#define 标识符 字符串

他是一个简单的物理替换,不做语法检查,后面不加;

可以用#undef命令终止宏定义的作用域

带参数的宏定义

#define S(a,b) a*b 

 与函数调用的区别:1、机械替换 2、不用定义类型,作为字符串替代 3、宏调用执行时间稍快

#include包含文件后所有的源文件编译成一个可执行文件

条件编译

C语言允许有选择地对程序的某一部分进行编译,对一部分源程序制定编译条件。可以将部分源程序不转化为机器码。

形式1:#ifdef 标识符 程序段1 #else 程序段2 #end if 含义:若标识符已被定义过(#define),用程序段1进行编译,否则编译程序段2

形式2:#ifndef 标识符 程序段1 #else 程序段2 #endif 含义:与形式1相反,当标识符没有被定义过(用#define定义)则对1进行编译,否则编译2 

形式3:#if 表达式 程序段1 #else 程序段2 #endif 含义:当表达式为真!0,编译程序段1,为0编译程序段2

采用条件编译后可以使机器代码程序缩短

程序的多文件组织

当一个完整程序被存放在多于一个文件中时,称为程序的多文件组织

内部函数和外部函数

内部函数仅限在本文件中调用,其他文件不能调用,用static定义该函数

外部函数可以被其他文件调用,用extern定义该函数。

第六章 数组

一维数组的定义和引用

数组是同一类型的一组值在内存中顺序存放。整个数组共用一个名字,其中每一项被称为一个元素

定义方式:

类型说明符 数组名【常量表达式】

1、序号从0开始 2、数组大小不能是变量 3、使用的时候其元素相当于变量

一维数组初始化

可以使用int a[]={0,1,2,3,4,5}编译器可以自动计算出内部的元素项数,并将数组定义为该长度

用局部static或全局定义的数组不赋值,系统默认是'\0'

static int a[10] 存储在静态数据区中的数组其元素默认为0,数组在内存中顺序存放,第一个元素位于地址的最底端

二维数组的定义和引用

定义方式 int a[3][4]

初始化:1、分行赋值 int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}

2、顺序赋值 int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}

3、部分赋值 int a[3][4]={{1},{5},{9}}

4、分行或全部赋值时,可以省略第一维,第二维不可以省略!a[][4]

数组作为函数参数

数组元素做参数

作函数实参,用法与一般变量作实参相同,为值传递

用数组名做函数参数

C++中数组名被认为是数组在内存中存放的首地址

数组名作函数参数,实参与形参都应用数组名,形参数组的大小可不指定。

函数传递的是数组在内存中的地址

实参形参共用一段内存,形参数组中的值发生变化,想打公寓实参数组中值发生变化

void sub(inta[])

用多维数组名作为函数参数

形参可以省略第一维,但不可以省略第二维,第二维必须与实参中的维数相等

int max_value(int array[][4])

字符数组

字符数组中的一个元素存放一个字符

字符数组的引用(使用循环)

字符串和字符结束标志:c++语言将字符串作为字符数组进行处理

      比如字符串常量:“CHINA”在机器内被处理成一个无名字符型一维数组

CPP约定\0为字符串的结束标志,占用内存空间但不计入串长度。

可以使用字符串的形式为字符数组赋初值

char c[]={"I am a boy"} 相当于 char A[]={'I','','A','M','','A','','B','O','Y'} 相当于char c[12]="i am a boy";

这里的c是字符数组在内存中存储的地址,一经定义,便成为常量,不可以再进行赋值

若数组定义长度大于字符串长度,后面都是\0

字符数组的输入输出

通常使用循环语句进行输出输入,对于一维字符数组的输入,在cin中仅仅给出数组名,输出cout也只给出数组名。

如果要把输入的一行作为一个字符串送到字符数组中,使用cin.getline(数组名,数组空间数)

字符串处理函数

调用#include <string.h>,注意所有字符串处理函数的实参都是字符数组名

1、strcat(str1,str2); 第一个字符串需要有足够空间才能接上str2

2、复制两个字符串函数strcpy(str1,str2);将第二个字符串复制给第一个

3、比较两个字符串strcmp(str1,str2);函数对字符串中ASCII两两比较,直到与遇上不同字符或\0

该函数具有返回值,是两字符串对应第一个不同的ascii码差值。若完全相同,函数值为0

4、求字符串长度函数strlen(str1),函数参数是数组名,返回值是数组首字母到'\0'的长度,并非在数组在内存中空间的大小长度,不包括'\0'

5、strlwr(str1)大写字母转化成小写

6、strupr(str1)小写字母转化成大写字母

7、strncmp(字符串1、字符串2、maxlen)限定最多比较的字符个数

8、strncpy(数组名1,字符串2,maxlen)限定最多拷贝字符个数

注意!两字符串之间不能直接进行比较,赋值,都需要通过字符串函数来实现。

debug方法:单步调试(选择是否进入子函数)or 运行到光标处 

第七章 结构体、共同体和枚举类型

结构体

定义:将不同类型的数据有序组合在一起,创造一个新的数据类型,称为结构体。是多种类型组合的数据类型。

struct 结构体名{成员列表};

结构体类型是一种数据类型,不占用内存空间,只有定义结构体类型变量的时候才开辟空间(机械替换);结构体变量在内存中依照成员顺序顺序排列,所占内存空间大小为全体成员所占空间总和

编译时,仅对变量分配空间,不对类型分配空间;对结构体中各个成员可以单独引用、赋值,作用和变量相同 student.num;结构体成员可以是另一个结构体的类型

结构体变量的引用

不能对结构体变量整体赋值或输出,只能分别对各个成员引用;但可以将一个结构体变量整体赋给另外一个相同类型的结构体变量student2=student1

嵌套结构的结构体变量必须逐层引用student.birthday.day;结构体变量中的成员可以同一般变量一样进行运算

对局部变量类型的结构体变量初始化

结构体数组 

每个元素都是一个结构体类型的变量,包括该类型的各个成员,数组各元素在内存中连续存放

 

 结构体类型的静态成员 

编译程序为该成员分配一个存储空间,这种结构体类型的所有变量共同使用这个成员的存储空间。eg:sturct s{static int id} int s::id=50;

若有定义s s1 s2,则变量s1,s2的id成员占用同一存储空间。必须在文件作用域中的某一个敌法国对静态的成员进行定义性说明且只能说明一次。

共用体

cpp中,予许不同的数据类型使用同一存储区域,即同一存储区域由不同类型的变量共同表示。这种数据类型是共用体。几个成员在共用体变量中存放在同一地址,相互覆盖,长度为最长的成员的长度。

特点:空间在某一时刻只有一个成员在起作用;成员是最后一次放入的成员;不能再定义时赋初始值;不能作为函数的参数或函数值,但可以使用指向共用体的指针变量;共用体可以作为结构体成员,结构体也可以作为共用体成员

枚举类型

若一个变量只有几种可能的值,可以定义为枚举类型。变量的值仅限于列举出来的值的范围内。

enum weekday{1,2,3,4,5,6,7}; enum weekday workday,weekend;

1、枚举元素为常量不可以赋值。2、定义枚举类型时,编译程序按照顺序给每个枚举元素一个对应的序号。从0开始后续依次+1。3、可以定义时人为指定枚举元素序号值{sun=2,mon=9,tue,wed} 4、只能给枚举变量赋枚举值,若要赋序号值必须进行强制类型转换。day=(enum weekday)1;

5、枚举元素可以用来进行比较判断if(workdat==mon) if(workday>sun) 6、枚举值可以进行加减一个整数n的运算,得到其前后第n个元素的值。7、枚举值可以按照整形输出其序号值。

第八章 指针和引用

指针

地址:内存区的字节编号

直接访问:按照变量地址存取变量的值。cin>>i;实际上放到定义i单元的地址中。

间接访问:将变量地址存放在另一个单元p中,通过p取出变量的地址再对变量进行操作。一个变量的地址称为该变量的指针。若程序定义一个变量或数组,那么这个变量的地址就是一个常量。

&i:取出i的地址 *指向

定义一个变量用来存放另一个变量的地址,称为指针变量(只能存放地址!)

int i,*i_point; i_point=&i;

指针变量引用

指向完了以后可以通过*a将内存空间中的变量取出

自增自减与指针(老忘)

 

指针变量作为函数参数:作用是将一个变量的地址传送到另一个函数中。变量作函数参数传递的是具体值,而指针作函数参数传递的是内存的地址。

用指针变量作函数参数,在被调函数执行过程中,应使指针变量所指向的参数值发生变化。这样函数的调用结束后变化值才能保留回主调函数。函数调用不能改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。

数组的指针和指向数组的指针变量

数组名是数组的起始地址,数组的指针就是数组的起始地址,因此数组名a和&a[0]效果一致。CPP规定p+1指向数组下一个元素而非下一个字节。

数组名作函数参数

 传递数组的地址,形参数组并没有额外开辟新的存储单元,这样的话形参数组变化实际上实参数组也发生变化。实参数组、实参指针、形参指针、形参数组可以对照使用,即首地址可以在被调函数中用一个指针变量接收。

指向多维数组的指针和指针变量(难)

a为二维数组名,a+1是a[1]地址,即第一行地址,所以a是行指针。

a[1]为一维数组名,a[1]+1是a[1][1]的地址,所以a[1]是列指针

指向由m个整数组成的一维数组的指针变量 int (*p)[m];

多维数组的指针作函数参数:注意实参究竟是行指针还是列指针,从而决定形参类型。

 字符串的指针和指向字符串的指针变量

string为数组名,代表数组的首地址是常量。

用字符指针表示字符串 

将内存中字符串常量的首地址赋给一个指针变量 

 字符串指针作为函数参数

字符数组名作参数或用指向字符串的指针变量作为参数 

 字符指针变量与字符数组

字符数组和字符指针变量都可以实现字符串的存储和运算,区别:

字符数组名称是常量,定义时必须指明占用空间的大小。

字符指针是变量,里面存储字符型地址,可以整体赋值,但字符串必须以\0结尾

函数的指针和指向函数的指针变量

可以用指针指向函数,用来存放函数的地址。函数在编译时被分配给一个入口地址。这个地址就称为函数的地址。也就是函数的指针。cpp语言规定,函数名代表函数的入口地址。

专门存放函数地址的指针变量称为指向函数的指针变量:

 函数名max代表函数在内存中的入口地址,是一个常量不可以被赋值。

指向函数的指针变量p可以先后指向不同的同种类型的函数。但不可做加减运算。

使用函数的指针调用函数

给指针变量赋值:p=max

通过指针变量调用实际上就是用p替换函数名

实参:实际的函数名(函数地址)

形参:指向函数的指针变量 与实参的类型完全一致(返回值,参数)

通用函数:所有的内部函数调用都用函数指针调用

返回指针的函数

被调函数的返回值不是数据而是一个地址,所以函数的类型是指针类型。

类型标识符 *函数名(参数表)

指针数组和指向指针的指针

一个数组,其元素都是指针类型的数据,称为指针数组。也就是说指针数组中的每一个元素都是指针变量,可以放地址。

类型标识: *数组名【数组长度说明】

常用字符指针数组,数组中的元素指向字符串首地址,这样比字符数组节省空间!

指向指针的指针变量

 

指针数组作为main()函数的形参 

 main函数也是可以带参数的!!main函数是dos系统调用的,所以main函数实参的值是在DOS命令行中给出的。

小结:指针变量可以有空值,即指针变量不指向任何地址,

两指针可以相减不可以相加。若要进行相减运算那么两指针必须指向同一数组,相减结果为相距数组元素个数。

指向同一数组的两个指针变量可以比较大小。 

 在内存动态分配存储空间

在定义变量或数组的同时就为其在内存开辟指定的固定空间(一经定义,在内存不能被其他的变量所占用)程序中我们有时需要根据实际需要开辟空间。

使用new运算符可以在程序中动态开辟内存空间,他相当于一个函数,在内存开辟玩空间后返回这个空间首地址。此时这个地址必须用一个指针保存下来才不会丢失。

用new开辟的内存单元没有名字,指向其首地址的指针是引用它的唯一途径。如果重新赋值,new开辟的内存单元就是内存中丢失了。

用new开辟的空间不能再分配空间时进行初始化。

用new开辟的内存如果程序不主动收回,那么就一直存在直到重新开机。

delete运算符可以讲动态分配的内存空间归还给系统呢。

引用

 对变量起另外一个名字,这个名字就叫做该变量的引用。

类型 &引用变量名=原变量名

其中原变量名必须是一个已定义过的变量

eg:int max int &refmax=max

refmax没有开辟新空间,只是引用max的单元,他俩就在内存中占用同一地址,即同一地址两个名字。

对于引用类型的变量:1、引用在定义的时候需要初始化。2、对引用的操作就是对被引用的变量的操作。3、引用类型变量的初始化值不能是一个常数。4、引用同变量一样有地址。可以通过动态分配的内存空间来初始化一个引用变量。

指针和引用的区别:

1、指针是通过地址间接访问某个变量,而引用是通过别名直接访问某个变量。

2、引用必须初始化,而一旦被初始化后就不得再作为其他变量的别名。

当&a前有类型符时,他必然是对引用的声明

引用和函数

引用的用途主要是用来做函数的参数或函数的返回值。

引用作函数的形参,实际上是在被调函数中对实参变量进行操作。

引用作为形参,实参是变量而不是地址,这和指针变量作为形参不一样。

函数的返回值是引用类型

引用函数相当于返回了一个变量,所以可以对其返回值进行赋值操作。这一点类似于函数的返回值是指针类型。

一个函数返回引用类型,必须返回某个类型的变量。注意:由于函数调用返回的引用类型是在函数运行结束后产生的,所以函数不能返回自动变量和形参。

返回的变量的引用,这个变量必须是全局变量或静态局部变量,即存储在静态区中的变量。

(一般而言,函数不可以作为左值,但是如果返回值是一个变量的别名,就可以用左值)

const类型变量

用const限制说明符时注意:一定要进行初始化,在说明时进行初始化是对这种常量置值的唯一方法

1)禁写指针 声明语句格式为:数据类型 *const 指针变量名

即pr将始终指向一个地址,成为一个指针变量。它将不能再作为左值而放在赋值号的左边。

2)禁写间接引用 :const 数据类型 *指针变量名

不可以通过指针对变量重新赋值

3)禁写指针又禁写间接引用

const 数据类型 *const 指针变量名

用指针处理链表

这数据结构的,回头复习去。

第九章 类与对象(面向对象的程序设计)

对象

概念:任何一个对象都应当具有:1、属性 2、行为

定义:对象是由一组属性和一组行为构成,每个对象都是由数据和函数(操作代码)两部分组成

我们对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,从外界看不到,而使用对象的人只需要了解外部功能就可以操作对象。

传统面向过程:所有数据都是公用的,一个函数可以使用任何一组数据,一组数据又可以被多个函数所使用。

面向对象程序设计:

每一组数据都有特定用途,是某种操作的对象。即一组操作调用一组数据。

面临困难:

1、设计所需要的各种类和对象,即决定哪些数据和操作封装在一起。

2、考虑怎样向有关对象发送消息,以完成所需的任务。

解决方法:

所以人们设想把相关的数据和操作放在一起,形成一个整体,与外界相对分隔。

对象=算法+数据结构 程序=(对象+对象+对象...)+消息

通过消息对对象进行控制。C++中对象的类型称为类class,代表某一批对象的共性和特征

类是抽象的对象,而对象是类的具体实例。

类是一种复杂的数据类型,是将不同类型的数据与这些数据相关的运算封装在一起的集合。

类将一些数据与其相关函数封装在一起,使其获得很好的保护

用关键字private限定私有成员在类的内部使用,只允许该类中的成员函数使用私有成员数据。

public的数据或函数不受类的限制,可以在类内或类外自由使用。

protected只允许在类内或该类的派生类中使用保护的数据或函数。

未加说明,默认访问权限为private;每一个限制词在类体中可以使用多次,一次使用一直有效,直到下一个限制词开始。

成员函数

可以在类体内定义成员函数,也可以在类体内说明成员函数原型,在类体外定义成员函数。

 定义时注意:1、类有封装性,类只定义一种结构,所以类中热河成员数据均不能使用关键字extern,auto或register限定存储类型。

2、定义类时只定义了一种导出的数据类型,并不为类分配存储空间,所以不能对其初始化。

 结构体变量是类的一个特例:在类中,成员的缺省的存储权限是私有的;在结构体类型中,成员的缺省的存储权限是公有的。

内联成员函数

类中直接定义函数体,成员函数编译时是作为内联函数来实现的

在类体外定义内联成员函数时,在成员函数的定义前面加上关键字inline

空间分配 

定义类时,只定义一种数据类型,不为类分配存储空间。在定义了属于类的变量后,系统才为类的变量分配空间。

因此我们称类的变量为对象。对象是类的实例。建立对象时,只为对象分配用于保存数据成员的内存空间,而成员函数的代码为该类的每一个对象所共享。

三种对象定义方式:全局、局部、初始化(private类型不能初始化)

引用成员数据和成员函数时同结构体变量用.符号,只能访问公有成员,若要访问对象的私有的数据成员,只能通过对象的公有成员函数来获取。

对象可以做函数的入口参数(实参,形参),也可以作为函数的出口参数。这与一般变量作为函数参数是相同的。

可以定义类类型的指针,类类型的引用,对象数组,指向类类型指针数组,指向一维或多维数组的指针变量。

一个类的对象,可以作为另一个类的成员。

类作用域,类类型的作用域和对象的作用域

类体的区域称为类作用域,类的成员函数与成员数据,作用域都是属于类的作用域,仅在该类的范围内有效。所以不能在主函数中直接通过函数名和成员名来调用函数。

类类型作用域:在函数定义之外定义的类,类名的作用域为文件作用域。函数体内定义的类,类名作用域为块作用域。

对象的作用域与前面介绍的比变量作用域完全相同。全局对象、局部对象、局部静态对象。

类的嵌套

定义一个类时,在其类体中又包含了一个类的完整定义,称为类的嵌套。类是允许嵌套定义的。

类的对象如何引用私有数据乘员631

1、通过公有函数为私有成员赋值

2、利用指针访问私有数据成员

3、利用函数访问私有数据成员

4、利用引用访问私有数据成员

成员函数的重载

类中的成员函数可以带有缺省的参数,也可以重载成员函数。

重载时,函数的形参必须在类型或数目上不同。可以有缺省参数的函数,若形参不完全缺省,必须从形参的右边开始缺省。

定义类的指针及如何用指针来引用对象

定义类的数组及数组中元素的引用 

返回引用类型的成员函数(可以返回私有数据成员的引用) 

 

利用面向对象思想改造线性表程序;

this指针:不同对象占据内存中的不同区域,所保存的数据不同,但对成员数据进行操作的成员函数的程序代码一致(系统自动将对象的指针带到成员函数中)

当对一个对象调用成员函数时,编译程序先将对象地址赋给this指针,然后调用成员函数。所以每次成员函数存取数据成员时,也隐含使用this指针。(注意!this指针里的地址是一个常量 

第十章 构造函数和析构函数

构造函数和析构函数是类体中说明的两种特殊的成员函数。

构造函数

构造函数在创建对象时,使用给定的值对对象初始化。析构函数功能相反,在系统释放对象前对对象做善后工作。

构造函数可以带参数,可以重载,没有返回值。

构造函数是类的成员函数,系统约定构造函数名必须和类名相同。构造函数提供了初始化对象的一个简单方法。

对于构造函数:1、构造函数的函数名必须与类名相同。

2、定义构造函数不能指定函数返回值类型,也不能指定为void类型

3、定义多个构造函数时必须满足函数重载原则。

4、构造函数可指定参数的缺省值。

5、若定义的类药说明该类的对象时,构造函数必须是公有函数的成员函数;若定义的类进用于派生其他类时,则可将构造函数定义为保护的成员函数;

因为构造函数属于类的成员函数,对于私有数据成员、保护的数据乘员和公有的数据乘员都能进行初始化。

 

对局部对象、静态对象、全剧对象的初始化 

局部对象:每次定义时都要调用构造函数。

静态对象:首次定义时调用,只需要调用一次。

全剧对象:在main函数执行之前调用构造函数。

缺省的构造函数 

 定义类时,若没有定义类的构造函数,编译器自动产生一个缺省的构造函数,格式为

className::className{}

缺省的构造函数不对所产生的数据成员赋初始值,即新产生的数据成员的值是不确定的。

 

 说明:1、在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数。

2、所有的对象在定义时必须调用构造函数“!!不存在没有构造函数的对象”

3、缺省的构造函数只能有一个

4、任一对象的构造函数必须唯一 

 构造函数和new运算符

可以使用new运算符动态建立对象,建立时自动调用构造函数,最后返回这个动态对象的起始地址。不再使用这种对象时,必须用delete运算符释放对象所占空间。用new建立类的对象时,可以使用参数初始化动态空间。

析构函数 

与构造函数相反,在对象生命期结束时,释放系统为对象所分配的空间,即撤销一个对象。 

 格式:ClassName::~ClassName(){/函数体}

1、析构函数是成员函数,可以写在类体内也可以写在类体外

2、析构函数是一个特殊的成员函数,函数名必须与类名相同,并在其前面加上字符~,以便和构造函数名区别开来

3、析构函数不能带有任何参数,不能有返回值,不指定函数类型。

4、析构函数不允许重载,一个类中只能定义一个析构函数。

5、他是在撤销对象的时候由系统自动调用的。先调用析构函数,然后再回收为对象分配的存储空间。(过程:调用缺省的构造函数;调用非缺省的构造函数;推出主程序;调用析构函数;调用析构函数)

如果对象使用new开辟空间,那么类中应该定义一个析构函数,并在其中使用delete删除由new分配的内存空间。因为撤销对象时西东自动回收对象分配的存储空间而不能回收new分配的动态空间。delete首先调用该对象的析构函数,再释放这个对象占用的空间。

 不同存储类型的对象调用构造函数和析构函数

1、对于全局定义的对象(函数外定义的对象)程序开始执行时,调用构造函数;程序结束时,调用析构函数。

2、对于局部定义的对象(函数内定义的对象)执行到定义对象的地方时,调用构造函数。推出对象作用域时调用析构函数。

3、用static定义的局部对象,首次到达对象时gz,程序结束时xg

4、对于用new动态生成的对象:系统不能自动地调用析构函数来撤销动态生成的对象。

 动态构造及析构对象数组

 用new运算符动态生成对象数组时,自动调用构造函数。用delete运算符释放p1所指向的对象数组所占用的存储空间时指针变量前加上【】才能将数组元素所占用的空间全部释放,否则只释放第0个元素所占用的空间。

缺省的析构函数

若没有显示定义析构函数,编译器自动产生一个缺省的析构函数

ClassName::~ClassName(){}

注意!要释放对象的数据乘员用new运算符分配的动态空间时必须显式定义析构函数。

实现类型转换的构造函数

 同类型的对象可以相互赋值,相当于类中的数据乘员相互赋值。若直接将数据赋给对象,所输入的数据需要强制类型转化,需要调用构造函数!

 

注意:构造函数只有一个参数时可以=强制赋值 

完成copy功能的构造函数 

 可以在定义一个对象的时候用另一个对象为其初始化,即构造函数的参数是另一个对象的引用

这种构造函数常为完成copy功能的构造函数。

 

 若没有定义完成copy功能的构造函数,编译器自动生成一个隐含的完成copy功能的构造函数,依次完成类中对应数据成员的copy

 

当时当类中数据成员使用new运算符,动态地申请存储空间进行赋初始值时,必须在类中显式定义一个完成copy功能的构造函数,以正确实现数据成员的复制。 

 这种情况下必须要定义完成copy功能的构造函数

 构造函数和对象成员

如果类A中包含类B的对象,那么对A初始化的同时还要对其成员数据类B的对象初始化。类A的构造函数中要调用B的构造函数。

 初始化对象成员的参数可以是表达式,也可以仅对部分对象成员初始化。

对于对象成员的构造函数的调用顺序取决于这些对象成员在类中说明的顺序,与他们在成员初始化列表中的顺序无关。

析构函数的调用顺序与构造函数正好相反。

第十一章 继承和派生类

重用性

继承性是面向对象程序设计中最重要的机制!!这种机制提供了无限重复利用程序资源的一种途径。可以扩充和完善旧的程序设计以适应新的需求。

C++所谓继承就是在一个已存在的类的基础上建立一个新的类。已存在的类称为父类or基类,新建立的类叫做派生类or子类。

一个派生类可以从一个基类中派生,也可以从多个基类派生。从一个基类派生的继承称为单继承,从多个基类派生的称为多继承。

通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且同时拥有旧的成员。我们称已存在的用来派生新类的类为基类。派生出来的新类叫做派生类。

派生类则除了继承基类的所有可引用的成员变量和成员函数外,还可另外定义本身的成员变量和处理这些变量的函数,由于派生类可继承基类的成员变量和成员函数,因此在基类中定义好的数据和函数等的程序代码可重复使用,这样可以提高程序的可靠性 

 当从已有的类中派生出新的类时,可以对派生类做以下几种变化:
1、 可以继承基类的成员数据或成员函数。
2、可以增加新的成员变量。
3、可以增加新的成员函数。
4、可以重新定义已有的成员函数。
5、可以改变现有的成员属性。
在C++中有二种继承:单一继承和多重继承。当一个派生类仅由一个基类派生时,称为单一继承;而当一个派生类由二个或更多个基类所派生时,称为多重继承。

派生可能改变基类的性质,公有派生、保护派生、私有派生。默认是私有派生。

公有派生 

私有派生

 

保护派生

 抽象类和保护的成员函数

 当定义一个类,只能用作基类来派生出新的类,而不能用这种类来定义对象时,称为抽象类。

将类的构造函数或析构函数的访问权限定义为保护时,这种类为抽象类。

当把类中构造函数或析构函数说明为私有时,所定义的类通常是没有实际意义的。一般情况下不能用它来产生对象也不能用它来派生类。

多重继承 

可以用多个基类来派生一个类,是单一继承的扩展。

初始化基类成员:

构造函数不能被继承,派生类的狗在函数必须调用基类的构造函数来初始化基类成员基类子对象。

冲突

  

支配规则

基类与对象成员

任一基类在派生类中只能继承一次,否则,会造成成员名的冲突 
若在派生类中,确实要有二个以上基类的成员,则可用基类的二个对象作为派生类的成员。
把一个类作为派生类的基类或把一个类的对象作为一个类的成员,在使用上是有区别的:在派生类中可直接使用基类的成员(访问权限允许的话),但要使用对象成员的成员时,必须在对象名后加上成员运算符“.”和成员名。

赋值兼容原则 

可以将一个派生类对象的地址赋给基类的指针变量。

 虚基类

 

这种同一个公共的基类在派生类中产生多个拷贝,不仅多占用了存储空间,还可能造成多个拷贝中数据的不一致和模糊引用。

多重派生过程中,若使公共基类在派生类中只有一个拷贝,则可以说这种基类是虚基类。

在基类类名前加上virtual就可以说明是虚基类。

 

用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则在每一个派生的构造函数中都必须有对虚基类构造函数的调用(且首先调用)。

第十二章 类的其他特性

友元函数

类中私有和保护的成员在类外不能被访问,是一种定义在类外部的普通函数,能够访问类中私有成员和保护成员。类的访问权限限制对其不起作用。

友元函数在类体内说明,前面加上关键字friend

友元函数不是成员函数,用法也与普通的函数完全一致,只不过它能访问类中所有的数据。友元函数破坏了类的封装性和隐蔽性,使得非成员函数可以访问类的私有成员。
一个类的友元可以自由地使用该类中的所有成员。

友元函数近似于普通的函数,它不带有this指针,因此必须将对象名或对象的引用作为友元函数的参数,这样才能访问到对象的成员。
与一般函数不同的是,1、友元函数必须在类的定义中说明,其函数体可在类内定义,也可在类外定义;2、它可以访问该类中的所有成员(公有的、私有的和保护的),而一般函数只能访问类中的公有成员。

使用友元函数要谨慎,通常通过友元函数取值而不修改对象中的成员值,则一定安全。

大多数情况是友元函数是某个类的成员函数,即A类中的某个成员函数是B类中的友元函数,这个成员函数可以直接访问B类中的私有数据。这就实现了类与类之间的沟通。

友元类 

不管是按哪一种方式派生,基类的私有成员在派生类中都是不可见的。
如果在一个派生类中要访问基类中的私有成员,可以将这个派生类声明为基类的友元。

虚函数

多态性是面向对象的程序设计的关键技术。

调用一个函数名,但可以根据需要实现不同功能。

多态性:编译时的多态性(函数重载)

运行时的多态性(虚函数)程序在执行前,根据函数名和参数无法确定调用哪个函数,必须在执行过程中根据具体情况动态确定。

可以将一个派生类对象的地址赋给基类的指针变量。

若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数,这样将不同派生类对象的地址赋给基类的指针变量后,就可以动态根据这种赋值语句调用不同类中的函数。

虚函数的定义和使用

可以在程序运行时通过调用相同的函数名而实现不同功能的函数称为虚函数。定义格式为:
virtual  <type>  FuncName(<ArgList>);

 一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数的特性。在派生类中重新定义基类中的虚函数时,可以不用关键字virtual来修饰这个成员函数 。
virtual修饰的某基类中的protected和public成员函数,可在派生类中重新定义。只有在程序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束。

(高内聚,低耦合)

注意事项⚠️:

1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同。若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成员函数。 

2、实现这种动态的多态性时,必须使用基类类型的指针变量,并使该指针指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现动态的多态性。
 

3、虚函数必须是类的一个成员,不能是友元函数,也不能是静态成员函数。

4、派生类中没有重新定义虚函数时,与一般成员函数相同,当调用这种派生类对象的虚函数时,调用其基类中的虚函数。

5、可以把析构函数定义为虚函数,但不能将构造函数定义为虚函数。

6、虚函数与一般成员函数相比调用时执行速度要慢一些。

7、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性,以实现“一个接口,多个形态”。

虚函数的访问

用基指针和对象名访问。用基脂针访问虚函数时,指向其实际派生类对象重新定义的函数。

通过一个对象名访问时,只能静态集束,即在编译器编译时决定调用哪个函数。 

纯虚函数

在基类中不对虚函数给出有意义的实现,它只是在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的目的地由不同的派生类中的对象决定。这个虚函数称为纯虚函数。
 

1、定义纯虚函数时,不能定义虚函数的实现部分。

2、在没有重新定义这种纯虚函数前是不能调用这种函数的 。

3、把至少包含一个纯虚函数的类,称为抽象类。这种类只能作为派生类的基类,不能用来说明这种类的对象。因为虚函数没有实现部分,所以不能产生对象。但可以定义指向抽象类的指针,即指向这种基类的指针。当用这种基类指针指向其派生类的对象时,必须在派生类中重载纯虚函数,否则会产生程序的运行错误。

4、在以抽象类作为基类的派生类中必须有纯虚函数的实现部分,即必须有重载纯虚函数的函数体。否则,这样的派生类也是不能产生对象的。
综上所述,可把纯虚函数归结为:抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性。

虚基类 

多基派生中的多条路径具有公共基类时,在这条路径的汇合处就会因对公共基类产生多个拷贝而产生同名函数调用的二义性。
解决这个问题的办法就是把公共基类定义为虚基类,使由它派生的多条路径的汇聚处只产生一个拷贝。

由虚基类派生出的对象初始化时,直接调用虚基类的构造函数。因此,若将一个类定义为虚基类,则一定有正确的构造函数可供所有派生类调用。
用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则在每一个派生类的构造函数中都必须有对虚基类构造函数的调用 (且首先调用)。

静态成员 

通常,每当说明一个对象时,把该类中的有关成员数据拷贝到该对象中,即同一类的不同对象,其成员数据之间是互相独立的。

当我们将类的某一个数据成员的存储类型指定为静态类型时,该类所产生的所有对象、其静态成员共享一个存储空间。这个空间是在编译的时候分配的。在说明对象时,并不为静态成员分配空间。

在类定义中,用关键字static修饰的数据成员称为静态数据成员。

注意事项⚠️:

1、类的静态数据成员是静态分配存储空间的,而其它成员是动态分配存储空间的(全局变量除外)。当类中没有定义静态数据成员时,在程序执行期间遇到说明类的对象时,才为对象的所有成员依次分配存储空间,这种存储空间的分配是动态的;而当类中定义了静态数据成员时,在编译时,就要为类的静态数据成员分配存储空间。

2、必须在文件作用域中,对静态数据成员作一次且只能作一次定义性说明。因为静态数据成员在定义性说明时已分配了存储空间,所以通过静态数据成员名前加上类名和作用域运算符,可直接引用静态数据成员。在C++中,静态变量缺省的初值为0,所以静态数据成员总有唯一的初值。当然,在对静态数据成员作定义性的说明时,也可以指定一个初值。

3、静态数据成员具有全局变量和局部变量的一些特性。静态数据成员与全局变量一样都是静态分配存储空间的,但全局变量在程序中的任何位置都可以访问它,而静态数据成员受到访问权限的约束。必须是public权限时,才可能在类外进行访问。

4、为了保持静态数据成员取值的一致性,通常在构造函数中不给静态数据成员置初值,而是在对静态数据成员的定义性说明时指定初值。 

静态成员函数

可以将类的成员函数定义为静态的成员函数。即使用关键字static来修饰成员函数 。
 

 静态成员函数用法说明:

1、与静态数据成员一样,在类外的程序代码中,通过类名加上作用域操作符,可直接调用静态成员函数。 
2、静态成员函数只能直接使用本类的静态数据成员或静态成员函数,但不能直接使用非静态的数据成员 (可以引用使用)。这是因为静态成员函数可被其它程序代码直接调用,所以,它不包含对象地址的this指针。 
3、静态成员函数的实现部分在类定义之外定义时,其前面不能加修饰词static。这是由于关键字static不是数据类型的组成部分,因此,在类外定义静态成员函数的实现部分时,不能使用这个关键字
4、不能把静态成员函数定义为虚函数。静态成员函数也是在编译时分配存储空间,所以在程序的执行过程中不能提供多态性。
5、可将静态成员函数定义为内联的(inline),其定义方法与非静态成员函数完全相同。

const、volatile对象和成员函数

用const修饰的对象,只能访问该类中用const修饰的成员函数,而其它的成员函数是不能访问的。用volatile修饰的对象,只能访问该类中用volatile修饰的成员函数,不能访问其它的成员函数。
当希望成员函数只能引用成员数据的值,而不允许成员函数修改数据成员的值时,可用关键词const修饰成员函数。一旦在用const修饰的成员函数中出现修改成员数据的值时,将导致编译错误

const对象

在成员函数的前面加上关键字const,则表示这函数返回一个常量,其值不可改变。 

其语义是指明这函数的this指针所指向的对象是一个常量,即规定了const成员函数不能修改对象的数据成员,在函数体内只能调用const成员函数,不能调用其它的成员函数。

 volatile对象

<type>  FuncName(<args>)   volatile

其语义是指明成员函数具有一个易变的this指针,调用这个函数时,编译程序把属于此类的所有的数据成员都看作是易变的变量,编译器不要对这函数作优化工作。 

由于关键字const和volatile是属于数据类型的组成部分,因此,若在类定义之外定义const成员函数或volatile成员函数时,则必须用这二个关键字修饰,否则编译器认为是重载函数,而不是定义const成员函数或volatile成员函数。

指向类成员的指针

在C++中可以定义一种特殊的指针,它指向类中的成员函数或类中的数据成员。并可通过这样的指针来使用类中的数据成员或调用类中的成员函数。 

指向类中数据成员的指针变量
定义一个指向类中数据成员的指针变量的一般格式为:
<type>  ClassName:: *PointName;
其中type是指针PointName所指向数据的类型,它必须是类ClassName中某一数据成员的类型 

注意:

1、指向类中数据成员的指针变量不是类中的成员,这种指针变量应在类外定义。

2、与指向类中数据成员的指针变量同类型的任一数据成员,可将其地址赋给这种指针变量,赋值的一般格式为:PointName = &ClassName::member;
这种赋值,是取该成员相对于该类的所在对象中的偏移量,即相对地址(距离开始位置的字节数)
如:mptr = &S::y;  
表示将数据成员y的相对起始地址赋给指针变量mptr。

3、用这种指针访问数据成员时,必须指明是使用那一个对象的数据成员。当与对象结合使用时,其用法为:ObjectName.* PointName

4、由于这种指针变量并不是类的成员,所以使用它只能访问对象的公有数据成员。若要访问对象的私有数据成员,必须通过成员函数来实现。

指向类中成员函数的指针变量

定义一个指向类中成员函数的指针变量的一般格式为:
<type>  (ClassName:: *PointName)(<ArgsList>);
其中PointName是指向类中成员函数的指针变量;ClassName是已定义的类名;type是通过函数指针PointName调用类中的成员函数时所返回值的数据类型,它必须与类ClassName中某一成员函数的返回值的类型相一致;<ArgsList>是函数的形式参数表。
在使用这种指向成员函数的指针前,应先对其赋值
PointName= ClassName::FuncName;
同样地,只是将指定成员函数的相对地址赋给指向成员函数的指针。

注意事项⚠️:

1、指向类中成员函数的指针变量不是类中的成员,这种指针变量应在类外定义。

2、不能将任一成员函数的地址赋给指向成员函数的指针变量,只有成员函数的参数个数、参数类型、参数的顺序和函数的类型均与这种指针变量相同时,才能将成员函数的指针赋给这种变量。 

3、使用这种指针变量来调用成员函数时,必须指明调用那一个对象的成员函数,这种指针变量是不能单独使用的。用对象名引用。

4、由于这种指针变量不是类的成员,所以用它只能调用公有的成员函数。若要访问类中的私有成员函数,必须通过类中的其它的公有成员函数。

5、当一个成员函数的指针指向一个虚函数,且通过指向对象的基类指针或对象的引用来访问该成员函数指针时,同样地产生运行时的多态性。

6、当用这种指针指向静态的成员函数时,可直接使用类名而不要列举对象名。这是由静态成员函数的特性所确定的。
 

第十三章 运算符重载

函数的重载

就是完成不同功能的函数可以具有相同的函数名。

1、定义重载函数必须具有不同的参数个数or参数类型。2、仅返回值不同不能定义为重载函数。

编译系统中的运算符“+”本身不能做这种运算,若使上式可以运算,必须重新定义“+”运算符,这种重新定义的过程成为运算符的重载。

运算符重载就是赋予已有的运算符多重含义。C++通过重新定义运算符,使它能够用于特定类的对象执行特定的功能

运算符的重载从另一个方面体现了OOP技术的多态性,且同一运算符根据不同的运算对象可以完成不同的操作。 
为了重载运算符,必须定义一个函数,并告诉编译器,遇到这个重载运算符就调用该函数,由这个函数来完成该运算符应该完成的操作。这种函数称为运算符重载函数,它通常是类的成员函数或者是友元函数。运算符的操作数通常也应该是类的对象。

重载运算符与一般函数的比较:

相同1)均为类的成员函数 2)实现同一功能

重新定义运算符,由左操作符调用右操作符。最后将函数返回值赋给运算结果的对象。

当用成员函数实现运算符的重载时,运算符重载函数的参数只能有二种情况:没有参数或带有一个参数。对于只有一个操作数的运算符(如++),在重载这种运算符时,通常不能有参数;而对于有二个操作数的运算符,只能带有一个参数。这参数可以是对象,对象的引用,或其它类型的参数。在C++中不允许重载有三个操作数的运算符。

只能对C++中已定义了的运算符进行重载,而且,当重载一个运算符时,该运算符的优先级和结合律是不能改变的。

单目运算符的重载

可以看出,虽然运算后对象a的值一致,但先自加或后自加的重载运算符函数的返回值不一致,必须在重载时予以区分。

用成员函数实现运算符的重载时,运算符的左操作数为当前对象,并且要用到隐含的this指针。运算符重载函数不能定义为静态的成员函数,因为静态的成员函数中没有this指针。

运算符重载为友元函数

实际:由一个操作数调用另外一个操作数。参与运算的对象全部成为函数参数。

=()【】->不能重载为友元函数

对双目运算符,重载为成员函数时,仅一个参数,另一个被隐含;重载为友元函数时,有两个参数,没有隐含参数。一般来说,单目运算符最好被重载为成员函数;对双目运算符最好被重载友元函数。

转换函数 

在类中定义一个成员函数,作用是将类转换为某种数据类型。

注意,转换函数只能是成员函数,不能是友元函数。转换函数的操作数是对象。转换函数可以被派生类继承,也可以被说明为虚函数。 

赋值运算符与赋值运算符重载= 

同类型的对象间可以相互赋值,等同于对象的各个成员的一一赋值。

A a(2,3) b b=a

但当对象的成员中使用了动态的数据类型时(用new开辟空间),就不能直接相互赋值,否则在程序的执行期间会出现运行错误。

此时,必须要重新定义=

 一个字符串类

在C++中,系统提供的字符串处理能力比较弱,都是通过字符处理函数来实现的,并且不能直接对字符串进行加法、减法,字符串的拼接,字符串之间的相互赋值等操作。可以通过应用C++提供的运算符重载机制,可以提供字符串的直接操作能力,使得字符串的操作与一般的数据一样方便。

可见,字符串类只定义了指针,并没有开辟具体的空间以存放字符串的内容,所以,无论是构造、析构还是加减等,均需要考虑动态开辟空间的问题,这也是字符串类的难点。 

若不定义字符串的析构函数,则可以不定义它的拷贝的构造及赋值函数,若定义了析构函数,必须重新定义这两个成员函数。

原则:!每个对象都有自己的独立空间。

第十四章 输入/输出流类库

 编译系统已经以运算符或函数的形式做好了对标准外设(键盘、屏幕、打印机、文件)的接口,使用时只需按照要求的格式调用即可。

输入输出流I/O stream 

 C++语言的I/O系统向用户提供一个统一的接口,使得程序的设计尽量与所访问的具体设备无关,在用户与设备之间提供了一个抽象的界面:输入输出流。

用标准流进行输入/输出时,系统自动地完成数据类型的转换。对于输入流,要将输入的字符序列形式的数据变换成计算机内部形式的数据(二进制或ASCII)后,再赋给变量,变换后的格式由变量的类型确定。对于输出流,将要输出的数据变换成字符串形式后,送到输出流(文件)中。 

在C++中允许用户重载运算符“<<”和“>>”,实现对象的输入和输出。重载这二个运算符时,在对象所在的类中,将重载这二个运算符的函数说明该类的友元函数。 

 

 

文件流fstream

程序对于文本文件的操作与对剑怕、显示器的操作比较:

在涉及文本文件的操作时,将输入文件看成键盘,将输出文件看成显示器,格式不变。只需在程序中增加打开与关闭文件的语句。

文件的操作

  

 文本文件的打开与关闭

在文件操作前,需要将程序与被操作的文件联系起来,使程序可以“引用”文件

在程序内定义一个文件类的对象,由该对象与文件发生联系,程序内所有的与文件的操作都是对该对象的操作。

 

如何发生联系? 

打开文件“myfile1.txt”用于输入,并将这个文件与输入文件类对象infile建立联系,今后,在程序中,用到这个文件“myfile1.txt”的地方就用infile来代替。
打开文件“myfile1.txt”用于输入,并将这个文件与输入文件类对象infile建立联系,今后,在程序中,用到这个文件“myfile1.txt”的地方就用infile来代替。

如何从文件中输入输出数据? 

当用类fstream定义文件对象时,该对象即能定义输入文件对象,又能定义输出文件对象,所以打开文件时,必须在成员函数open()中的参数中给出打开方式(读或写)。 

 

在打开文件后,都要判断打开是否成功。若打开成功,则文件流对象值为非零值;若打开不成功,则其值为0。 

打开输入文件时,文件必须存在。
打开输出文件时,若文件不存在,则建立文件;若文件存在,则删除原文件的内容,使其成为一个空文件。 

字符串文件的读写操作

 二进制文件的读写操作

若在文件的打开方式中没有特别说明,打开的文件均为ASCII码文件,若要打开二进制文件,则要特别说明并用特定的读写函数。 

 

二进制文件不是ASCII码,不能直接对其读写,必须要通过特定函数进行转换。

  判断二进制文件是否读到句尾?

infile.eof();到了,返回非零。else0

文件指针

 当一打开文件,文件指针位于文件头,并随着读写字节数的多少顺序移动。

 可利用成员函数随机移动文件指针

fine 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/76327
推荐阅读
相关标签
  

闽ICP备14008679号