《面向对象程序设计(C++描述)》学习笔记

复制的话会改变格式,直接把OneNote的图片粘了过来,原文字在底层

边学边写hhh(终于搞完了)

表示OIer以前没学过面向对象555


一、c++基础知识

1.1 面向对象的程序设计语言(c++ Java vb)略

1.2 变量

1.2.1 变量的定义方法 局部变量、全局变量、块变量

1.2.2 const修饰符

1.2.3 引用类型

引用就是给已经存在的变量(或对象)起一个别名,即引用引入了一个变量(或对象)的同义词。一个引用类型的变量,其作用相当于指针,但不允许像指针那样进行一些操作,从而可以提高程序的性能。

例如:int x = 3; int &y = x;表示x与y实际上是一个变量,两者表示完全相同的内存空间。

引用与指针的差别在于指针可以操纵两个实体,一个是指针值,一个是指向的值,因此指针可以改变关联的实体,而引用只能操纵一个实体。引用和指针都能够实现一个存储空间的多途径操纵,但是由于指针存在安全隐患,一般情况下多用引用来实现函数参数的设计。

1.3 函数

1.3.1 函数原型

1.3.2 内联函数

如果一个函数前面加有关键字inline,这样的函数被称为内联函数。系统在编译时,凡遇到调用内联函数,则在用实参替代形参后,用该函数中的全部代码来替代内联函数的调用表达式,而不是像一般函数那样在运行时被调用。

注意:a,在内联函数内不允许用循环语句和开关语句 b,书中出现的类结构中所有在类说明内部定义的函数是内联函数。

1.3.3 带默认参数的函数

void fun(int x, int y = 0)//参数y的默认值为0,当调用语句给出函数的参数时,就按参数调用函数,当调用语句未给出函数的参数时,就按该默认参数调用该函数。(非默认参数写在默认参数前面,否则会出错)

调用时可以写成fun(1,2),也可以写成fun(1),这样y默认为0。

1.3.4 函数的参数传递

函数调用时,编译系统才给形参分配实际的存储空间,并将对应的实参的值一次性传给形参,函数调用结束后,系统释放形参所占用的存储空间。

1 按值传递 只是实参值向形参的单向传递。

2 按地址传递 将实参的地址传递给形参 实参必须是地址值,形参用指针变量。发生函数调用时将实参的值直接传递给对应的形参变量,形成了实参和形参共同作用于一个存储空间的情况。 为双向传递。

3 按引用传递 引用可以作为函数的返回值类型和参数,使用引用作为函数的形参时,主调函数的实参需使用变量名。实际上是被调用函数使用了实参的别名,因而形参值的改变就是对实参值的改变,也是双向传递。

1.3.5 函数重载

函数重载指同一个函数名可以对应着多个函数的实现,也就是多个函数可以拥有相同的名字。进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同,否则,将无法实现重载(因为函数实现时,要求从函数参数的个数和类型上来划分)。

函数重载时要避免使用默认参数,否则引起二义性(即不知道和哪个函数匹配)。

Int add(int x, int y){return x+y;}

Int add(int x, int y, int z){return x + y + z;}

Double add(double x, double y){return x + y;}

调用时:

A = add(1,2);

B = add(1,2,3);

C = add(1.0,2.0);

会分别调用

1.4 名字空间

为了避免在大规模程序的设计以及在程序员使用各种各样的c++库时,标识符的命名发生冲突,标准c++引入了关键字namespace,可以更好地控制标识符的作用域。名字空间就像一个文件夹,其内的对象就像一个个文件,不同的文件夹里的文件可以重名,只需要说明是哪个文件夹下的就行了。

 

1.5 动态内存分配

动态存储空间,是指系统提供的一个称为堆的很大的内存区域。动态申请的内存空间地址通常通过赋值语句赋值给一个指针类型的变量,这样,就可以通过指针操作来完成需要的数据存取,也便于不再使用时用delete运算符把该内存空间动态释放给系统。

1.5.1 内存申请

1 new 数据类型;(int *p = new int;)

2 new 数据类型(初始化值);(int *p = new int (3);)

3 new 数据类型【常量表达式】;(char *str = new char[50];)

因为系统的内存资源有限,所以并不是每次申请空间都能成功,如果申请不到空间,new操作符会返回一个NULL值,所以在申请时需要判断。

1.5.2 内存释放

1 delete 指针名 (int *p = new int; delete p;)(int *p = new int (3);delete p;)

2 delete [ ]指针名 (char *str = new char[50]; delete [ ]str;)

注意点:

1 new和delete必须配套使用,如果搭配错了程序运行时会产生不可预知的错误。

2 在用delete释放指针指向的内存空间时,必须保证这个指针指向的空间是用new申请的,并且该空间只能释放一次。

3 如果在程序中使用new申请了空间,就应该在结束程序前释放所申请的空间。

4 如果一个指针的值是Null,那么释放这个指针也不会引起错误,所以当一个指针没有指向合法的空间,或者指针所指向的空间已经释放掉,最好将指针的值设定为NULL,这样避免错误的使用这个指针。

1.6 作用域

1.6.1 生存期

生存期与存储区密切相关,C++用户使用的存储区域主要包括存放程序的代码区,存放程序全局数据和静态数据的全局数据区、用于支持函数调用、暂时存储信息的栈区和存放程序动态数据的堆区,对应的生存期分别为静态生存期、局部生存期和动态生存期。

程序中具有静态生存期的变量若没有进行初始化,在编译时系统自动给变量置初值为0。

1.6.2 全局和局部变量

程序中若没有对局部变量进行初始化,系统不会对其初始化,其初值具有不确定性。·

1.7 多文件结构

有利于软件开发的分工与合作,当全部源程序完成后,再进行编译、链接和运行。

 

二、面向对象程序设计概述

2.1 面向对象程序设计的基本概念

2.1.1 面向对象方法的产生

2.1.2 面向对象方法的方法与结构

2.1.3 类和对象的概念

对象由一组属性和一组行为构成,对象只有在具有属性和行为的情况下才具有意义。属性是用来描述对象静态数据特征的数据项,行为是用来描述对象动态特征的一系列操作。

2.1.4 消息与事件的概念

2.2 面向对象程序设计的特点

2.1.1 抽象性

包括数据抽象和行为抽象

2.2.2 封装性

在c++语言中,类的成员的访问权限分为私有、保护和公用三种。外部程序对一个类中具有不同访问权限的成员具有不同的可见程度,并由公用访问权限的成员对外提供服务。

2.2.3 继承性

c++中子类对父类方法的继承,有三种不同形式:完全继承、扩充继承、覆盖继承。

2.2.4 多态性

2.3 面向对象的软件开发

2.3.1 面向对象分析

1 确定类(抽象类和对象类,包含有纯虚函数的类为抽象类,反之为对象类)

2 确定类的属性

类的属性:实例属性、类属性

类的方法:常规性方法(辅助性方法)、功能性方法(需求性方法)

3 确定对象模式

组合模式、一般——特殊模式(继承)、消息模式(关联关系)

2.3.2 面向对象设计

1 修改和完善对象模型,主要包括对类做修改和补充,对对象模式做必要的调整,如重新设计类的继承关系等。

2 在对对象模型反复修改的基础上,写出所有类的定义。

3 构造基于特定计算机环境的软件系统所要求的额外的、与实现有关的类和对象模型

4 实际的软件系统要求的其他设计。

在面向对象的设计过程中,应遵循的软件工程中关于软件设计的基本准则:模块化、抽象、信息隐藏、低耦合和高内聚、可扩充、可重用;还需要考虑具体实现中的基于特定计算机环境的人机接口、数据管理和系统接口等因素。

2.3.3 面向对象实现

2.3.4 面向对象测试

包括面向对象分析的测试、面向对象设计的测试、面向对象编程的测试、面向对象的单元测试、面向对象的集成测试、面向对象的系统测试等环节。

2.3.5 面向对象软件维护

 

三、类与对象

3.1 类

3.1.1类的定义

一般形式:

class 类名{

private:

私有的成员函数

私有的数据成员定义

protected:

保护的成员函数

保护的数据成员定义

public:

类的共有接口

};

3.1.2 访问控制

1 私有 private 只能被该类的成员函数访问。

2 保护 protected 只能被该类的成员函数或派生类的成员函数访问。

3 共有 public 可以在类外访问 也被称为类的接口。

3.1.3 成员变量

声明成员变量

(private/protected/public:

    数据类型 成员变量名)

成员变量的设计

成员变量不能在定义时初始化。

成员变量不能递归定义(成员变量不能用自己所属的类型来定义自身的成员变量)。

3.1.4 成员函数(类的方法)

1 成员函数定义

内部:private/protect/public:

函数返回值 函数名称([参数列表])

{

函数体

}

外部:要在成员函数前面加上 ”类名::“告知编译器这是这个类的成员函数。

2 成员函数重载

3 成员函数的行为限制

为避免外部功能单元对类的数据成员产生误操作,可通过const关键字对成员函数进行行为限制。

当成员函数的某个参数修饰为const时,表示该参数在成员函数内不会也不能被修改。

当成员函数修饰为const时,表示限制该成员函数只能读取当前对象的成员变量,但不能修改当前对象的成员变量。

用const修饰成员函数时,关键字const既可以放在成员函数定义的最前面,也可以放在成员函数的最后面,通常放在最后面(Fun fun(const Fun w)const;)。

3.2 构造函数和析构函数

构造函数的功能是在创建对象时,使用给定的值来将对象初始化;析构函数的功能时用来释放一个对象的。

3.2.1 构造函数

构造函数是在对象被创建时由系统自动调用的成员函数。当定义对象时,需要在内存为对象开辟空间,并对对象内部的成员变量赋初值,构造函数便充当了这一角色。

1 构造函数的特点

[<类名>::]<类名>(<形参表>)

{

函数体

}

① 构造函数的命名必须和类名完全相同,而一般方法不能和类名相同。

② 构造函数不能有返回类型,即使是void也不行。这是由于构造函数返回类本身的对象,因此不需要给出构造函数返回值类型。

③ 构造函数的参数用来传递定义对象时的初始值,通常情况下要注意参数的类型与成员变量的类型相匹配。

④ 构造函数的参数允许使用默认值。构造函数没有默认值时,不能定义数组对象。

⑤ 构造函数的访问权限一定是公有的。

⑥ 构造函数可以重载,即允许一个类中有多个参数个数或参数类型不同的构造函数。

2 默认的构造函数

每个类必须有一个析构函数,若程序没有提供任何构造函数,则c++提供一个默认的构造函数,其为无参构造函数,仅负责创建对象,不做任何初始化的工作。

3.2.2 析构函数

当对象脱离其作用域时系统自动执行析构函数。它不能带任何参数,也没有返回值,且只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个默认析构函数,它不进行任何操作,所以一般简单类中没有显式的析构函数。

1 析构函数的特点

[<类名>::]~<类名>( )

{

函数体

}

① 析构函数名是在类名前再加字符~。

② 析构函数不能带任何参数,不能有返回类型(即使是void也不行)。

③ 一个类中只能有一个析构函数。

④ 析构函数的访问权限一定是公有的。

⑤ 函数体为空的析构函数可以省略不设计。此时,系统将自动定义一个默认的、函数体为空的析构函数。

2 默认的析构函数

[<类名>::]~<类名>( )

{

 

}

一般情况下,若类中没有用new运算符动态申请内存空间,则该类的析构函数为空。反之一定不为空,应该用delete运算符动态释放内存空间,否则会造成不再被程序使用的内存空间没有被系统回收,造成内存泄漏。

3.2.3 拷贝构造函数

在某些情况下,需要用已存在的对象初始化同类的另一个对象,这时需要一种特殊的构造函数,拷贝构造函数,也称复制构造函数,它的参数是本类对象的引用。

class 类名

{

public:

类名(参数表);

类名(const 类名 &对象名);

};

类名::类名(const 类名 &对象名)

{

函数体;

}

在三种情况下拷贝构造函数会被系统自动的调用:

① 用类的一个对象初始化该类的另一个对象时;

② 对象作为一个函数实参传递给函数的形参时;

③ 对象作为函数的返回值返回给调用者时。

3.2.4 浅拷贝与深拷贝

1 浅拷贝是指由默认的拷贝构造函数所实现的数据成员逐一赋值。通常情况下,默认的构造函数是能够完成这项工作的,如果类的内部含有指针类型的的数据,默认构造函数的做法会使程序出现错误。

2 深拷贝:为了解决浅拷贝出现的问题,必须定义一个符合要求的拷贝构造函数,使之不但拷贝数据成员,而且为对象分配各自的内存空间,这种情况称之为深拷贝。

3.2.5 构造函数和析构函数的调用过程

3.3 对象

3.3.1 对象的定义

3.3.2 类成员的访问

类成员的访问形式

在访问权限合法的前提下,外界对类成员的访问有圆点访问形式和指针访问两种形式。

圆点访问形式采用的是成员运算符“.”,格式为:

对象名.公有成员

指针访问形式使用成员访问运算符“->",格式为:

对象指针变量名->公有成员 或 (*对象指针变量名).公有成员

3.4 子对象

当一个类的成员是另一个类的对象时,该对象就成为子对象。

3.4.1 组合模式

面向对象程序设计语言中,表示组合模式的方法是首先定义表示部分概念的类,然后再定义表示整体概念的类,并在定义整体概念的类中,定义一个部分概念类的成员变量,通常把这种成员变量叫做子对象。整体概念类就是通过子对象来调用部分概念类的成员的。

3.4.2 子对象和构造函数设计

当一个类中存在子对象时,系统无法自动调用相应的构造函数为子对象赋初值,通常的做法是显式调用相应的构造函数为其赋初值。

3.4.3 内部类

当类A完全是为类B服务的,外部程序不需要也没有必要创建类A的对象时,类A可以定义在类B的内部。定义在内部的类不能再在main函数中定义对象。

3.5 静态成员

为了实现一个类的不同对象之间的数据和函数共享,提出了静态成员的概念。

3.5.1 定义与引用

3.5.2 静态数据成员(在某种程度上破坏了类的封装性)

静态成员变量不是定义在每个具体对象时都单独具有的成员变量,而是整个类只有一个的成员变量,所以静态变量不能用构造函数初始化,必须单独初始化。

静态成员变量在主函数外(包括类外)初始化,语句格式为:

<类名>::<static 成员变量名>=<初始化值>;

3.5.3 静态成员函数(不会破坏类的封装性)?

3.6 友元

3.6.1 友元的作用

在有些时候需要在类的外部访问类的私有成员,为此需要一种途径,在不放弃私有成员的安全性的前提下,使得一个普通函数或类的成员函数可以访问到封装到某一个类中的信息,用友元作为实现这个需求的手段。

3.6.2 友元函数

3.6.3 友元类(将这个类中的所有成员函数都定义为另一个类的友元)

3.6.4 友元成员函数

3.7 设计举例

 

四、继承与派生

4.1 继承和派生的概念

4.1.1 继承的层次结构

4.1.2 继承的作用

4.2 派生类

4.2.1 派生类的定义

class<派生类名>:[private|protected|public]<基类名>

{

<派生类成员变量和成员函数定义>

};

多重继承:

class<派生类名>:[继承方式1][<基类名1>],[继承方式2][<基类名2>]

{

<派生类成员变量和成员函数定义>

};

4.2.2 派生类的三种继承方式

公有继承:基类中的公有成员和保护成员分别成为派生类的公有成员和保护成员。

私有继承:基类中的公有成员和保护成员成为派生类的私有成员。

保护继承:基类中的公有成员和保护成员成为派生类的保护成员。

4.2.3 派生类对基类成员函数的继承

成员函数的完全继承

成员函数的覆盖继承

成员函数的扩充继承

4.2.4 派生类的构造函数和析构函数

基类中的构造函数和析构函数不能被派生类继承。在设计派生类的构造函数时,显式调用基类的构造函数,为派生类对象的基类子对象赋初值。

4.3 多重继承

一个类别可以从多个父类继承特征和行为。

4.3.1 多重继承的设计方法

4.3.1 多重继承的二义性问题

4.3.3 虚基类

所谓虚基类,就是说在一个类层次中,如果某个派生类存在一个公共基类,则系统只考虑最原始的那个公共基类。因此,虚基类方法可以消除成员变量的二义性问题。

在多重继承中,构造函数按照下列顺序进行调用:

1 任何虚拟基类的构造函数按照他们被继承的顺序进行调用。

2 任何非虚拟基类的构造函数按照他们被继承的顺序进行调用。

3 任何成员变量的构造函数按照他们声明的顺序进行调用。

4 类自己的构造函数。

4.4 赋值兼容规则

1 可以用派生类对象为基类对象赋值。

2 可以用派生类对象初始化基类的引用。

3 可以把派生类对象的地址赋给基类对象的指针。

4 可以把指向派生类对象的指针赋给基类对象的指针。

4.5 设计举例

 借阅问题 单链表问题 出圈问题

五 多态性

5.1 多态性的实现类型

多态性可以分为四类:参数多态 包含多态 重载多态 强制多态(前二为通用多态 后二为专用多态)

包含多态也称作运行时的多态,是研究类族中定义于不同类中的同名成员函数的多态行为,主要是通过虚函数来实现的。

重载多态体现为普通函数重载、成员函数重载和运算符重载。

强制多态是指将一个变元的类型加以变化,以符合一个函数或操作的要求(例如两个不同类型的数相加时进行的强制类型转换)

5.2 联编

联编实际上是将一个标识符和一个存储地址联系在一起的过程。c语言中,所有的联编都是静态联编,c++中,一般情况下也是静态联编,但是一旦涉及多态性和虚函数就必须使用动态联编。

5.2.1 静态联编

静态联编是指在编译阶段玩成的联编的方式,主要优点是函数调用速度快、效率高,不足之处是编程不够灵活,函数重载和运算符重载是通过静态联编方式实现的重载多态。

5.2.2 动态联编

优点是大大增强了编程灵活性、问题抽象性和程序易维护性。缺点是函数调用速度慢。

5.3 虚函数

在程序代码中要指明某个成员函数具有多态性需要进行动态联编,且需关键字virtual来标记,这就是虚函数。

5.3.1 虚函数的声明

虚函数是一个在基类中通过virtual声明,并在一个或多个派生类中被重新定义的成员函数。一个成员函数一旦被声明为虚函数,则无论声明他的类被继承了多少层,在各层次的派生类中,该函数都保持虚函数的特性。

声明虚函数时应注意:

1 只有在类层次中才可以设计虚函数,只有一个类没有设计虚函数的必要。

2 派生类中声明的虚函数,其成员函数名、参数个数、参数类型以及返回类型,要和基类中声明的虚函数的函数名、参数个数、参数类型以及返回类型完全一样。

3 由于使用虚函数时要利用赋值兼容规则,而赋值兼容规则要求派生类必须为公有继承,因此要求派生类必须由基类的共有继承方式产生。

5.3 2 虚函数的调用

5.3.3 虚析构函数

5.4 抽象类

如果一个类不能生成对象,则这个类是抽象类。c++语言中用纯虚函数方法设计抽象类,抽象类一般都是类层次中的基类。

纯虚函数是在类中声明为虚函数,并且在声明他的类中没有函数体定义的成员函数。如果一个类中存在一个或一个以上的纯虚函数,则称该类为抽象类。

5.4.1 纯虚函数的定义

格式:virtual<函数原型>=0;

5.4.2 抽象类的应用

5.5 运算符重载

即使运算符只在当前程序中定义了一次,也称作运算符重载,是因为系统中对所有的运算符都已有过定义。

5.5.1 运算符重载规则、

1 重载的运算符要保持原运算符的意义。

2 只能对已有的运算符重载,不能增加新的运算符。

3 不是所有的运算符都允许重载。

4 重载的运算符仍保持原先系统规定的优先级和结合性。

运算符重载一般有两种形式:一种是类的成员函数形式,一种是友元函数形式。

5.5.2 运算符重载为类的成员函数

对于“+”: objectX.operator+(objectY);

5.5.3 运算符重载为类的友元函数

对于“+”:operator+(objectX,objectY);

5.6 设计举例

字符串问题 学校教职工问题 借阅问题

 

六、模板

模板是一种将数据类型作为参数生成函数和类的机制,是实现代码复用的一种手段。

6.1 模板概述

6.1.1 参数多态性

简单说来,就是让参数在后期绑定。编译时并不考虑其类型,而在实现一个具体的实例时,编译器才知道其类型。

6.1.2 模板

    参数实例化

函数模板————>模板函数(n个)

6.2 函数模板

6.2.1 函数模板的定义

template<class T1[,class T2][,。。。]>

<返回类型><函数名>(<形参表>)

6.2.2 函数模板的使用

外部程序使用函数模板时,要先对函数模板的模板形参进行实例化。调用函数模板时,系统将根据调用函数的实际参数的数据类型自动实例化函数模板为模板函数。

例子:

template<class T>

T max(T x, T y)

{
 

 

}

If(x>y)

return x;

else return y;

 

6.2.3 模板函数的重载

6.3 类模板

6.3.1 类模板的定义

template<class T1[,class T2][,…]>

class<类名>

{

<类模板定义>

};

6.3.2 类模板的使用

6.3.3 类模板的继承

6.4 设计举例

 

七、异常处理

抛出异常 捕捉异常 处理异常

7.1 异常概述

7.1.1 异常的基本类型

常见的异常情况:用户输入错误 设备故障 物理限制(内存) 代码错误

7.1.2 传统的异常处理方法

 传统的条件判断 分支处理

7.2 c++的异常处理方法

7.2.1 基本的异常处理方法

异常信息的识别和传递是通过程序中定义的异常类来完成的。大部分情况下,异常类可以为空,只用来识别各个不同异常的标志。

Throw抛出 try捕捉 catch处理

7.2.2 多个异常处理方法

7.3 非空异常类的设计

7.4 异常抛出和处理的两种方式

1 在一个函数中抛出异常,在调用该函数的函数中处理异常。

2 在同一个函数中抛出异常和处理异常。

 

八、流类库

8.1 c++基本i/o流库的层次结构

8.1.1 i/o流类的概念

8.1.2 ios类的层次关系

8.1.3 streambuf类的层次关系

8.2 i/o格式控制

8.2.1 ios类成员函数的格式控制

8.2.2 操作符的格式控制

8.3 文件的i/o操作

8.3.1 文件的打开与关闭

8.3.2 文本文件的输入输出

8.3.3 二进制文件的输入输出

8.3.4 随机文件的输入输出

8.4 自定义数据类型的i/o操作

8.4.1 输出运算符重载

8.4.2 输入运算符重载

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值