C++概览

软件开发的六个阶段

软件计划可行性分析、工作范围和代价
需求分析(做什么)功能需求、性能需求、环境需求与限制
软件设计(怎么做)概要设计和详细设计
软件编码用具体语言实现设计
软件测试单元测试和综合测试
软件维护已交付的软件修改、扩充、排错

结构化程序设计

  • 总体结构:层次树状
  • 局部组织:模块化
  • 一般不允许goto
  • 程序:处理数据的一系列过程:数据与函数分开存储
  • 分而治之
  • 缺点:数据和任务分离,难理解、维护、重用性差

C语言特点

  • 结构化的程序设计语言
  • 兼具高级语言与汇编语言的特点
  • 程序的可移植性好:源码级别的可移植性
  • 语法结构不严密,有自由度

面向对象程序设计

  • 菜单、按钮、窗口:事件驱动
  • 数据和数据处理过程:对象
  • 面向对象的五大支柱:抽象、封装、数据隐藏、继承和多态性
  • C语言是贝尔实验室的 Dennis Ritchie 开发的,1972年在一台DEC PDP-11实现:用于UNIX系统的开发语言
  • 满足复杂性要求,1980年 贝尔实验室的 Bjame Stroustrup 开发带类的C:1983年正式取名C++
    在这里插入图片描述
  • C++成功的奥秘:面向对象程序设计;解决了程序员的烦恼,也就解决了程序编写过程的难题
  • C++语言特点
C++是C语言的超集
面向对象的程序设计语言
程序设计的可扩充性强
类、对象
重载(函数与操作符)
异常
引用
内存
模板

面向对象的思想与方法

  • 面向对象是一种认知方法学:从人们的认识过程来看,主要有以下两种方法
    (1) 从一般到特殊的演绎方法
    (2)从特殊到一般的归纳方法
  • 从一般到特殊的演绎方法:在分类到一定程度后,用面向对象的方法,就是对已经分好的各类对象进行状态描述和功能定义,以明确这一类对象所能完成的工作,其实这也是一种分类。最后一步就是怎样让这一类对象运转起来,也就是使各类对象建立联系,完成它们应有的功能
  • 从特殊到一般的归纳方法:面向对象提供了从一般到特殊的演绎手段(如继承等),又提供了从特殊到一般的归纳形式(如类等),从而说明它是一种很好的认知方法
  • 这种分类、归纳的方法在面向对象设计技术中是很重要的

"软件IC"的概念

  • 在软件工程中要提高软件生产率,就应当走类似硬件的道路,应该有软件的 “集成电路”:称软件 IC
  • 必须寻找一种能够比较容易地将正确成熟的软件单元应用于新的软件系统中的技术
  • 最好有一种集成机制,可以将已经成熟的软件单元制成一个相对独立的实体,使它们可以不加改动或很少改动就可以应用于新的软件系统中
  • “软件 IC”的概念就是基于这一思想的一种软件集成机制。“软件IC”是一种可重用模块

软件可重用性的问题

  • 避开许多非技术障碍,来考虑软件开发中的重复性质。编程人员一次又一次地重复编制一些基本模式:分类、搜索、读写、分配、同步、比较、……但这些事情并不是每次都一模一样的,有如此之多的细节需要变化。因此,软件工程师大多情况是这样做的:时时不断地在同一主题上,精心雕琢一个个新的变种
  • 针对软件重复的这种性质,如何寻找合适的可重用模块,以达到不变应万变 的能力

类概念支持 “软件 IC” 技术

  • 在面向对象语言中,类充当了系统构造的单元,这是因为它具有以下特点
    (1)类是一类对象的统一模板,它具有很强的模块性。类的功能代码实现只通过外部接口与外界联系,具有很强的独立性
    (2)类的可靠性表现在一个类的出错不会传播到其他类中
    (3)类是对象一级的抽象,它将一类对象的数据结构和功能实现封装起来,使得外界不必清楚其内部实现,只要从它的说明中了解其基本功能就可以使用它
  • 类支持代码共享,类中的代码均是可再入的,这也支持了可重用性。因此类作为 “软件 IC” 来组织软件系统从概念上是非常合适的

面对对象系统的特性

  • 抽象性
  • 封装性
  • 继承性
  • 多态性

FAQ:VC++ vs C++

  • VC++是一个编译器;C++是一种语言,是一个标准,一个符合C++语言标准的程序可以在任何支持标准C++的编译器上编译(如VC++,TC++和BC++)
  • VC++是与系统相关的,而C++则与系统无关
  • VC++不仅能编译C++程序,还能编译C程序。VC能编写Windows程序,C++并没有为特殊的操作系统提供任何支持。两者之间的关系是具体实现与标准的关系,也是物理与逻辑的关系。VC++是C++语言载体的一种

数据类型

  • 低级语言对数据(存储单元)没有进行抽象,使用指令进行操作
  • 高级语言对数据(存储单元)进行抽象,使用运算符进行操作
  • 数据类型:一组数据和对数据的操作的抽象(操作通常不和数据放在一起)
  • 对数据类型的使用,是通过对类型的实例(实际例子,也称为实体)的使用来体现的
  • 一般数据类型(内部类型):数据之间没有结构上、组织上或存储上的必然联系,并隐蔽了数据在存储单元中的基本表示
  • 结构数据类型(数据结构,用户定义类型):数据之间有必然联系,具有较高级别的抽象,由低级别的抽象实现,可以对基本表示的成分进行操作:数据与操作是分开的
  • 抽象数据类型:允许在合适的语言单位建立与表示有关的操作,用于实现这个类型,对使用这个新类型的单位而言,该类型的表示是隐蔽的
  • 类类型:包括的数据与操作是封装在一起的,在定义数据时,必须将对它们的操作也一起定义
  • 类型的作用:确定分配空间的大小和所能进行的操作:类型是给编译器用的:在exe可执行文件中不存在任何类型的信息
  • 基本数据类型:char、int、float、double、void(无值)
  • 基本数据类型的修饰符:signed, unsigned, long, short, const

对象定义

  • 面向对象语言的所谓“对象”,是将某组数据和对该组数据的一组基本操作封装在一起的实体
  • 对象与数据类型是有关系的,对象是一种抽象数据类型:类类型的实体
  • 对象是抽象数据类型的实现
    (1)抽象是一个系统的简化描述,它强调了系统的某些特性,而忽略了系统的其他特性。对于用户而言,所关心的是系统的功能,而不关心系统的组成和实现的细节
    (2)抽象数据类型将数据和操作一起进行了定义和封装,我们将抽象数据类型抽象为能向外提供的功能的接口
class Date {
	年、月、日的数据定义;
	对日期数据的操作定义;
};

对象的性质

  • 将密切相关的数据和过程封装起来定义为一个实体
  • 定义了一个实体后,即使不知道此实体的功能是怎样实现的,也能使用它们:这一点类似库函数的情况

对象核心概念

  • 对象的程序设计语言,最核心的概念是数据封装、继承和多态性。
  • 数据封装将一组数据和与这组数据有关的操作集合封装在一起,形成一个能动的实体,称为对象。用户不必知道对象行为的实现细节,只需根据对象提供的外部特性接口访问对象
class Font {
private:
	字形数据;
public:
	操作字形数据的成员函数;
};
  • 在这样建立的类 Font 中,能确保私有的字形数据只能由类中的成员函数进行访问和处理

封装

  • 封装是相对的
  • 类内无封装:成员之间都可见
  • 类外才有封装
类外不可见:数据定义 + 操作的实现
类外可见:操作的原型,即接口

继承

  • 继承是面向对象语言的另一个重要的概念。在客观世界中,存在着整体和部分的关系(a part of )、一般和特殊的关系(a kind of ),继承将后者模型化
  • 在面向对象语言中,类功能支持这种层次机制。除了根结点外,每个类都有它的超类(superclass),又称父类或基类。除了叶结点外,每个类都有它的子类(subcalss),又称派生类
  • 一个子类可以从它的基类那里继承所有的数据和操作,并扩充自己的特殊数据和操作
  • 基类抽象出共同特征,子类表达其差别。有了类的层次结构和继承性,不同对象的共同性质只需定义一次,用户就可以充分利用已有的类,符合软件重用的目标

多态性

  • 面向对象另外一个核心概念是多态性。所谓多态,是指一个名字,有多种语义;或更特殊:一个界面,有多种实现。利用重载(或称为超载)实现多态性;又分为运算符和函数重载
  • 在C语言中,运算符*代表了乘法和指针运算:这就是运算符重载
  • C++中
void f(int, int, char);
void f(char, float);  
void f(int, int);  
void f(float, float);
  • 这几个函数都具有相同的函数名f,但其参数不同,它们的函数体也可以完全不同,编译能根据其函数参数的不同而自动选择相应的函数体进行匹配。因此,一个函数名代表了多种函数的实现(函数体):一词多义

面向对象的CRC方法

Class name:对象取名

  • 对象取名实质上就是将系统的功能进行划分:分散责任
  • 学会在对象之间分散责任是开始朝向“thinking like an object”的第一步

Responsibilities: 责任

  • 每个对象完成设计中的一个小目标的功能,它实现了整个系统状态的某一部分

Collaborators:合作者

  • 每个对象可能会依赖其他对象来完成其责任。这个对象所依赖的对象集合称为它的合作者

过程调用与消息传递

  • 在结构化程序设计中,过程为一独立实体,显式地被它的使用者所见,而且,对于相同的输入参数,每一次过程调用,其输出结果是相同的
  • 在面向对象的程序设计中,方法(过程或操作)是隶属于对象的,它不是独立存在的实体,而是对象的功能体现。从对象实现机制看,对象是一台自动机,其中私有状态表示了对象的状态,该状态只能由对象的操作改变它
  • C++语言用成员函数实现操作。每当需要改变状态的时候,只能由其他对象向该对象发送消息(在C++中,借助于成员函数调用来实现消息发送)
  • 对象响应消息后,按照消息模式找出匹配的方法,并执行该方法(C++语言是执行函数调用)。应该注意,发送消息和过程调用的意义是不同的,发送消息只是触发自动机,同样的输入参数,可能因自动机状态不同,其输出结果也不同。因此,同一消息的多次发送可能产生不同的结果

数据类型和表达式

  • 引用:一个变量说明为另一变量的引用, 则成为对应变量的别名。必须初始化,且初始化它的变量必须是已声明过类型的变量。两者为同一存储单元
  • 引用的本质:T &ptr = var; 等价于T * const ptr = &var; 引用就是常指针
  • 引用存在的理由就是函数传参的简化
  • new和delete运算符和C中的malloc和free函数的区别:new运算符根据对象的类型,自动决定其大小,而malloc要指定分配存储空间的大小;new返回指向此类型的指针,malloc返回指向void*类型的指针
  • 工程文件(.dsw, .prj)
    (1) 告诉编译器可执行文件由哪些.c文件装成
    (2)连上哪些.obj文件
    (3)要连的标准库.lib
  • 程序运行时的内存占用:代码区、数据区、堆区、栈区
    在这里插入图片描述
  • 存储类型:extern、static、register、auto
  • 静态的:分配的存储空间直到整个程序结束才收回(全局变量特性);局部于某一作用域(局部变量特性)
  • 按作用范围可分:静态局部变量,局限于特定函数,但出作用域并不释放;静态全局变量,局限于它的源文件
  • 局部静态变量,作用域而言是局部,但生存期而言与全局变量相同,编译时分配单元并初始,程 序执行不初始化
  • 寄存器的:用CPU中寄存器提高频繁使用量的速度
  • register变量:每次从内存中取操作数,速度慢,若将中间变量放在 register 里可大大提高速度,但 register 个数有限,所以只有局部变量和形参可定义成 register 变量。如:循环控制变量
  • 自动的:函数的局部变量,出函数自动消失,一般省去auto:局部变量不加说明均是自动变量
  • 全局变量也允许其它文件使用,但在要引用它的文件中,用 extern 说明
  • 某些全局变量不允许其他文件使用. 则在该变量前冠以 static
  • 把局部变量改变为静态局部变量后,改变了它的存储方式,即改变了它的生存期,生存期为整个源程序。但作用域仍仅限于定义它的函数内,其它函数是不能使用它的
  • 把全局变量改变为静态全局变量后,改变了它的作用域,限制了它的使用范围,其作用域为定义它的源文件内,其它源文件中的函数是不能使用它的;但生存期仍为整个源程序
  • 关键字“static”在不同的地方所起的作用是不同的
  • 函数也可以声明为静态的,局部于它所在的文件,叫内部函数
  • 外部函数:作用在整个程序中的函数,用extern 说明

函数参数传递

  • 参数传值是参数值的拷贝,而不是参数本身
  • 函数要改变调用它的函数的变量有两种方法:指针调用和引用调用。当形参和实参结合时,复制的是变量的地址
  • 预编译处理语句:文件包含、宏定义、条件编译
  • 在C++中常用const变量和内联函数来代替宏和带参宏
  • 带参数的宏定义和函数的实现的区别
    (1)宏定义为替代和展开,没有增加系统的调用开销,仅能实现较简单的功能
    (2)函数:增加了系统的空间和时间的调用开销
  • 内联函数接合了两者的优点
    (1)在一般函数定义前面加上保留字inline,该函数即被说明为内联函数:inline关键字加函数实现上,不是函数声明上
    (2)对于内联函数,C++直接用函数体代码来替代对函数的调用,这一过程称为函数体的内联展开:使用内联函数增加了目标程序的代码量,增加了空间开销,但比使用函数节约了时间开销。因此,内联函数是以增加目标代码的尺寸来节省时间:以空间换时间
  • 使用内联函数注意的问题
    (1)内联函数必须在调用之前声明或定义
    (2)内联函数不能包含复杂的控制语句:如:while,for, switch,递归调用等
    (3)nline指令只是对编译器的一种提示,而不是一个强制性命令:即:编译器自由决定要不要忽略inline命令,如果含有循环、switch、递归调用,则编译器视之为普通函数(通过编译,不会报错),将其作为普通函数处理
  • 编译器在所有调用这个函数的地方将其实际的代码装上
  • 内联函数要出现在每一调用该函数的源文件之中,所以一般放在头文件中

函数重载

  • C中求绝对值函数
int iabs(int i);
long labs(long l);
double fabs(double d);
  • C++用同一个函数名 abs( )实现上面3个函数,用函数参数来区别到底调用哪个函数
  • 把同一作用域内名字相同,但参数不同的函数称为重载函数:这得益于函数原型不仅给出函数名,而且指出了参数类型
  • 重载函数通过参数表(参数个数,参数类型)的不同来区分。但不能通过函数返回类型来区分同名函数:主调函数可以忽略被调函数的返回值
  • 缺省参数也是一种重载方式,是函数调用方式的重载
void f(int x = 0); // x称之为缺省参数

引用:作为函数参数 vs 作为函数返回值

类与对象

  • 类由成员构成:成员变量 vs 成员函数
  • 成员变量:描述对象的属性
  • 成员函数:描述对象的方法
  • 类的封装
    (1)将一个数据与这个数据有关的操作集合封装在一起,形成一个能动的实体,称为对象
    (2)用户不必知道对象行为的实现细节,只须根据对象提供的外部特性接口访问对象
class 类名 {
public: 	// 公有作用域
	公有成员;
protected: 	// 类作用域 & 子类作用域
	保护成员;
private: 	// 类作用域
	私有成员;
}; // 分号不能省略
  • 匿名类:如果一个类没有名字的类称之为匿名类
class { // 匿名类: 该类不能使用: 既没有类名称,也没有实例化对象
}; // 但是这个类是合法的

class { // 定义匿名类以及定义一个该类的对象
	string name;
	unsigned char age;
} obj1;
  • 空类
class Empty { // 空类: 没有任何成员的类
};
// 空类实例化的对象仍然占据1个字节的空间: 因为对象具有唯一标识
Empty o1, o2;// 正是因为空类占据1个字节内存空间: o1与o2是两个独立的对象
  • 空类没有任何成员,包括数据和函数。但空类对象大小不为零。这意味着可以分配一个空类 的对象,并且各对象具有不同的地址

构造函数与析构函数

  • 它们是特殊的成员函数:构造函数是一个类的特殊成员函数,它的函数名与类名相同,它可以有任意类型的参数,但 不能具有返回类型。当创建一个对象时,系统(编译器)自动调用构造函数(它不能显示调用)。构造函数可缺省,也可以由用户定义
  • 构造函数用于初始化对象的成员
  • 析构函数用于清除对象
  • 定义构造函数要注意的问题
    (1)构造函数的名字必须与类名相同
    (2)构造函数没有返回值,在声明和定义构造函数时是不能说明它的类型的
    (3)构造函数的功能是对对象进行初始化,因此在构造函数中只能对数据成员做初始化,这些数据成员一般均为私有成员,在构造函数中一般不做赋初值以外的事情
  • 重载构造函数:与一般成员函数一样,C++允许重载构造函数。若类X具有一个或多个构造函数,创建类X的 对象时,编译器就会根据参数表的不同而调用其中的一个
  • 构造函数可具有缺省参数
    (1)像所有函数一样,构造函数可具有缺省参数
    (2)允许构造函数带缺省参数常用来把对象数组进行初始化:对象数组要求对象所属的类必须提供不用传递参数的构造函数
    (3)在使用具有缺省参数的构造函数时,要谨防二义性
  • 拷贝构造函数:已有对象初始化对象
    (1)构造函数的参数可以是任何类型参数,甚至可以将自己的类的引用作为参数,称它为拷贝构 造函数。形如: X∷X(const X & obj)
    (2)拷贝构造函数有两个含义,首先,它是一个构造函数,当创建一个新对象时,系统自动调用 它;其次,它将参数代表的对象逐域拷贝到新创建的对象中。C++编译器可以为类产生一个缺省的 拷贝构造函数,用户也可以根据自己的需要定义拷贝构造函数
    (3)类对象的拷贝还可能发生在函数参数传递对象和函数返回对象的情况下
  • 析构函数:与构造函数对应的是析构函数:构造函数的取反。析构函数没有返回类型,没有参数,函数名是类名前加“~”,它的作用为:
    (1)执行析构函数体(一般没有具体的工作)
    (2)可以使用完全限定名方式显式地调用析构函数;若没有,则在一个对象的作用域结束时,系 统自动调用析构函数
class Point {
public:
	~Point() {
		cout << "析构函数" << endl;
	}
};

Point pt;
pt.~Point(); // 显式调用析构函数
  • 析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载。只是在类对象生命期结束的时候,由系统(编译系统:编译器)自动调用
  • 构造函数与析构函数的特点
    (1)它们都没有返回值说明,也就是说定义构造函数和析构函数时不需指出类型
    (2)它们不能被继承
    (3)和大多数c++函数一样,构造函数可以有缺省参数
    (4)析构函数可以是虚的(virtual),但构造函数不行
    (5)不可取它们的地址
    (6)不能用常规调用方法调用构造函数;当使用完全的限定名(带对象名,类名和函数名)时可以调用析构函数
    (7)当定义对象时,编译程序自动调用构造函数;当删除对象时,编译程序自动地调用析构函数
  • 构造函数的种类
    (1)无参数的构造函数
    (2)带参数的构造函数
    (3)缺省参数的构造函数
    (4)默认构造函数
    (5)重载构造函数
    (6)拷贝构造函数(引用)
  • 带参数的构造函数:建立对象时,常常需要通过传递一定的数据,来对类中的各种数据成员初始化,使得初始化不再一成不变
  • 缺省参数的构造函数:些构造函数的使用中,只有特别的情况需要传递参数,一般都使用缺省值
  • 缺省参数的构造函数注意事项
    (1)如果函数所带的参数中,有一部分可以缺省,而有一部分不可缺省,所有取缺省值的参数必须出现在不取缺省值的参数的右边
    (2)缺省参数并不只用于构造函数,一般的成员函数,甚至于一般的全局函数都可以使用缺省参数
  • 默认构造函数
    (1)C++规定,每个类必须有一个构造函数,没有构造函数,就不能创建任何对象
    (2)若未提供一个类的构造函数(一个都未提供),则C++编译器提供一个默认的构造函数,该默认构造函数是个无参构造函数,它不做任何工作
    (3)只要一个类定义了一个构造函数(不一定是无参构造函数),C++编译器就不再提供默认的默认构造函数。也就是说,如果为类定义了一个带参数的构造函数,还想要无参构造函数,则必须自己定义
    (4)与变量定义类似,在用默认构造函数初始化对象时,如果创建的是全局对象或静态对象,则对象的位模式全为0,否则,对象值是随机的
  • 拷贝构造函数

静态成员

  • 关键字 static 可以用于说明一个类的成员
  • 静态成员提供了一个同类对象的共享机制
  • 把一个类的成员说明为 static 时,这个类无论有多少个对象被创建,这些对象都共享这一个 static 成员
  • 静态成员属于类,它不是属于对象的,不会随对象的消失而消失

静态成员变量

  • 一个类中,若将一个数据成员说明为 static ,则该数据称为静态数据成员
  • 无论创建多少个类对象,都只有一个静态数据的拷贝
  • 当这个类的第一个对象被创建时,所有 static 数据初始化为 0 ,并且不作其他初始化
  • 在类中,静态成员变量的声明不是定义,必须在类说明之外提供定义,以分配存储空间和进行初始化

静态成员函数

  • 当一个成员函数被说明为 static ,称为静态成员函数
  • 静态成员函数首先是一个成员函数,其次它又是一种特殊的成员函数
  • 静态成员函数仅属于一个类,不属于某一个特定的对象,因此它不能像一般的成员函数那样随意的访问对象中的非静态的数据内容
  • 一般的成员函数都含有一个this指针,用来指向对象自身;而在静态成员函数中是没有this指针的:由于静态成员函数可以在没有声明类的任何实例(对象)之前就被执行,因此它们不能使用this指针
  • 静态成员函数不是对象成员没有this指针,在静态成员函数的实现中,不能直接引用类的非静态成员
  • 静态成员函数使用的原因
    (1)像静态成员变量一样,我们也可以创建一个静态成员函数,它为类的全体服务而不是为一个类的部分对象服务
    (2)不需要定义一个全局函数,减少了全局或局部名字空间的占用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值