null回顾与分析——结构化程序
回顾与分析——结构化程序设计求圆的面积问题
学生成绩问题
结构化程序设计:
1)支持三种基本语句结构;
2)支持自顶向下、逐步求精、模块化程序设计方法
程序 = 数据(结构)+操作(算法)核心结构化程序设计的优点与缺陷结构化程序设计的优点与缺陷(1)优点 ①整体思路清楚,目标明确。 ②设计工作中阶段性非常强,有利于系统开发的总体管理和控制。 ③在系统分析时可以诊断出原系统中存在的问题和结构上的缺陷。 (2)缺点 ①对较复杂系统,难以控制,用户要求难以在系统分析阶段准确定义,致使系统在交付使用时产生许多问题。 ②系统的可扩展性较差。不能适应事物变化的要求。 ③软件的可重用性差。 课程总体安排课程总体安排理论:32学时
实验:24学时第1章 类和对象 **第1章 类和对象 重点掌握内容:
面向对象程序设计的基本特点
类和对象的定义和使用
类的设计基本方法第1章 类和对象**第1章 类和对象1.1 面向对象程序设计概述
1.2 面向对象方法的基本特征
1.3 类和对象的定义
1.4 类的成员函数
1.5 对象成员的引用1.1 面向对象程序设计概述**1.1 面向对象程序设计概述一、什么是面向对象程序设计
面向对象程序设计(Object Oriented Programming----简称OOP)是软件系统设计与实现的新方法,这种新方法即吸取了结构化程序设计的全部优点,又考虑了现实世界与面向对象空间的映射关系,所追求的目标是将现实世界的问题求解尽可能的简单化。
二、面向对象程序设计的基本思路1.2 面向对象方法的基本特征 **1.2 面向对象方法的基本特征
1.2.1 对象和类
1.2.2 封装(encapsulation)与数据隐藏
1.2.3 继承(inheritance)与重用
1.2.4 多态性(polymorphism)
1.2.5 消息
1.2.6 面向过程与面向对象程序设计方法的比较 1.2.1 对象和类 **1.2.1 对象和类 一、现实世界与面向对象系统对应关系
1.2.1 对象和类**1.2.1 对象和类二、什么是类(class)
1. 类是一种复杂的数据类型,它将不同类型的数据和与这些数据相关的操作封装在一起。类是对现实世界客观事物的抽象。
2. 两方面的要素:
属性(atrribute)---数据
行为(behavior) -----数据的操作 (函数)
三、什么是对象(object)
对象是类的一个实体,又称为实例1.2.1 对象和类**1.2.1 对象和类四、对象与类的关系人类
姓名
年龄
身高
体重
……
走路
学习
说话
开车
……数据抽象……小王小李小张人类的对象1.2.1 对象和类**1.2.1 对象和类五、对象与对象之间消息传递
1.2.2 封装与数据隐藏 **1.2.2 封装与数据隐藏 一、什么是封装(encapsulation)
封装是指将对象的数据和与这个数据有关的操作放在一起。
各个对象之间相互独立,互不干扰。
对象只留少量的接口,以便与外部联系。从外部看对象就像一个“黑匣子”,数据和方法是隐蔽的、看不见的。
当用户使用对象时,不必知道对象的具体实现细节,只需要根据对象提供的外部接口访问对象即可。1.2.2 封装与数据隐藏**1.2.2 封装与数据隐藏二、封装应具有以下几个特点
具有一个清楚的边界,私有成员封装在内部,外部不可以访问
提供必要的接口
对象内部的数据和方法是受封装外壳保护的,其它对象不能直接使用。
主机
主板
显卡
声卡
内存
数据传输线
电源线
……USB接口串行接口电源开关
……内
部
私
有
成
员与
外
部
接
口1.2.3 继承与重用 **1.2.3 继承与重用 一、继承(inheritance)概念的提出
例如:已定义电视机类,现需要定义彩色电视机类
解决方法:
1.重新设计
2.继承:继承原来电视机的属性和方法,再添加彩色电视机自己特有的新的属性和行为
1.2.3 继承与重用**1.2.3 继承与重用 二、继承的概念
C++提供的继承机制提供了类之间相互关系的解决
,使某个类可以继承另外一个类的特征和能力。
使用继承符合人们对事物的认识和叙述,大大简化了对问题的描述,提高了程序的可重用性,从而提高了程序设计、修改、扩充的效率,实现软件重用(software reusability)。
1.2.4 多态性(polymorphism) **1.2.4 多态性(polymorphism) 一、多态性的概念
多态性:描述的是同一个消息可以根据发送消息对象的不同采用不同的行为方式。
例如:
学校的上课铃响,不同班级的学生进入不同的教室学习,不同的老师进入不同教室开始讲课,不同的对象会作出不同的响应。可以看到学生和教师在接受到同一消息(上课铃声)时,执行不同的操作,这就是多态的表现。1.2.4 多态性(polymorphism)**1.2.4 多态性(polymorphism)二、C++支持多态
在c++程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用同一个函数名调用不同内容的函数,或指发出同样的消息被不同类型的对象接收时产生不同的执行结果和行为。1.2.5 消息**1.2.5 消息 面向对象技术的封装使得对象相互独立,各个对象要相互协作实现系统的功能则需要对象之间的消息传递机制。消息是一个对象向另一个对象发出的服务请求,进行对象之间的通信。也可以说是一个对象调用另一个对象的方法(Method)或称为函数(Function)。
1.2.6 面向过程与面向对象程序设计方法的比较**1.2.6 面向过程与面向对象程序设计方法的比较一、面向过程程序设计方法
程序 = 数据结构+算法
二、面向对象程序设计方法的规律是:
对象 = (数据结构+ 算法)
程序 = 对象 + 对象 + 对象 + …… + 对象1.3.1 类的定义 **1.3.1 类的定义 一、类定义组成
数据成员:说明类的属性
成员函数(又称函数成员):对类数据成员操作的类内函数,又称为方法。
二、类定义的一般格式:
class 类名
{
public : 数据成员和成员函数实现
protected/private : 数据成员和成员函数实现
};1.3.1 类的定义**1.3.1 类的定义说明:
1)定义一个类时,使用关键字class;类名必须是一个合法的变量名;
2)一个类包括类头和类体两部分。
3)大括号中定义的是类的数据成员和成员函数,称为类体(class body)。类定义结束要用“;”结束;
4)关键字public、 protected和private称为成员访问限定符。用访问限定符声明了各个数据成员和成员函数的访问权限1.3.1 类的定义**1.3.1 类的定义三、访问限定符
公有(public):提供了与外界的接口功能。公有成员可以被本类中的成员使用和访问,还可以被类作用域内的其他外在其他函数使用。
私有(private):封装在类内部的,只能被该类的成员和该类友元函数或友元类访问,任何类以外的函数对私有成员的访问都是非法的。
保护(protected):访问权限介于私有和公有中间,类的成员可以访问,类以外的其他函数不能访问保护成员
1.3.1 类的定义**1.3.1 类的定义1.3.1 类的定义**1.3.1 类的定义例:定义一个日期类Cdatenull**class Cdate //声明一个类,类名为Cdate
{
public: //以下是公有成员函数
void Setdate (int y, int m,int d) //设置日期
{
year=y;
month=m;
day=d;
} //以上3行是函数的实现
}null**int Isleapyear()
{
return (year%4==0&&year%100!=0)||(year%400==0);
}
void Print()
{
cout<
class Cdate
{
public:
void Setdate (int y, int m,int d)
{//在类内定义成员函数
year=y;
month=m;
day=d;
} 1.4.1 在类内定义成员函数**1.4.1 在类内定义成员函数 int Isleapyear()
{ return (year%4==0&&year%100!=0)
||(year%400==0);
}
void Print()
{ cout<数据成员名//“->”是指向运算符
对象指针名 -> 成员函数名
例如:
Cdate d,*p; //定义对象d和对象指针变量p
p=&d; //初始化对象指针p
p->Print();//通过指向对象的指针和指向运算符访问对象中的成员
1.5 对象成员的引用**1.5 对象成员的引用(3)通过对象的引用变量访问对象中的成员
如果为一个对象定义了一个引用变量,它们是共占同一段存储单元的,因此可以通过引用变量来访问该对象中的成员。
例如:
Cdate d1; //定义对象d1
Cdate& d2=d1;//定义引用变量d2
d2. Print(); //引用变量访问对象中的成员null**// Cdate.h 在头文件中声明日期类
class Cdate
{
public:
void Setdate (int y, int m,int d);
int Isleapyear();
void Print();
private:
int year,month ,day;
}; 【例1.1】一个完整的Cdate类的应用程序null**//Cdate.cpp 定义成员函数实现部分
#include
#include “Cdate.h” //包含类定义的头文件
void Cdate:: Setdate (int y, int m,int d)
{
year=y; month=m; day=d;
}
int Cdate::Isleapyear()
{ return (year%4==0&&year%100!=0)||
(year%400==0);
}
void Cdate::Print()
{ cout< Setdate(2000,4,2); //通过指针访问成员
d4. Setdate(2006,12,1); //通过引用访问成员
d1.Print(); p-> Print(); d4.Print();
}null**【例1.2】封装一个学生类,学生的信息包括学生的姓名、学号、性别、年龄和3门的成绩。可以设置学生的各类信息,统计该学生的平均成绩并输出学生信息。 Cstudent的类图 null**//CStudent.h 这是头文件,在此文件中进行类的声明
class CStudent //类声明
{
public:
void setinfo(char *,int ,char,int);
void setgrad(float , float , float);
float avg();
void display();
private:
char name[20];
int id ;
char sex;
int age;
float grad[3];
};null**//CStudent.cpp在此文件中进行函数的定义
#include
#include
#include "CStudent.h"//漏写此行编译通不过
void CStudent::setinfo(char *na,int i ,char s ,int a)
{
strcpy(name,na);
id=i; sex=s; age=a;
}
void CStudent::setgrad(float a, float b, float c)
{
grad[0]=a; grad[1]=b; grad[2]=c;
}
null**float CStudent::avg()
{
return (grad[0]+ grad[1] +grad[2])/3;
}
void CStudent::display( )
{
cout<<"id:"<
#include "CStudent.h"//将类声明头文件包含进来
void main( )
{
CStudent stud1; //定义对象
stud1. setinfo("张三",2006102,'m',18);
stud1.setgrad(79,98,87);
stud1.display();
cout <<"平均成绩:"<
class Cdate //定义一个日期类
{
public: //公有数据成员
Cdate() //定义构造函数
{
year=2000; //初始化数据成员
month=1;
day=1;
}
null** void Print() //一般成员函数
{ cout<
class Cdate
{
public:
Cdate(int y, int m,int d);
void Print();
private:
int year,month ,day;
};
Cdate::Cdate(int y, int m,int d)
{
year=y; month=m; day=d;
}null**void Cdate::Print()
{
cout<
class Cdate
{
public:
Cdate(); //不带参数的构造函数,又称默认构造函数
Cdate(int y); //带1个参数的构造函数
Cdate(int y, int m); //带2个参数的构造函数
Cdate(int y, int m,int d); //带3个参数的构造函数
void Print();
private:
int year,month ,day;
};null**Cdate:: Cdate() //构造函数的定义
{ year=2000; month=1; day=1;}
Cdate:: Cdate(int y)
{ year=y; month=1; day=1;}
Cdate:: Cdate(int y, int m)
{ year=y; month=m; day=1;}
Cdate:: Cdate(int y, int m,int d)
{ year=y; month=m; day=d;}
null**void Cdate::Print()
{cout<规定:
(1)每个类必须有一个构造函数,如果没有就不能创建任何对象;
(2)若没有定义任何一个构造函数,C++提供一个默认的构造函数,该构造函数没有参数,不做任何工作,相当一个空函数,例如:
Cdate::Cdate()
{
} 1.6.2 构造函数的重载**1.6.2 构造函数的重载 所以在讲构造函数以前也可以定义一个对象,就是因为系统提供的默认构造函数。
(3)只要C++提供一个构造函数(不一定是没有参数的),C++不再提供默认的构造函数。也就是说为类定义了一个带参数的构造函数,还想要创建无参的对象时,则需要自己定义一个默认构造函数
1.6.3 默认参数的构造函数**1.6.3 默认参数的构造函数为什么使用默认参数的构造函数:对象常有一些初始值。
【例1.6】设计CPoint类,它带有默认参数的构造函数。
分析:一个点包括横坐标(x)和纵坐标(y)两个数据成员, 默认初始值为(0,0)。也可以根据用户需要,初始化点为其他坐标,因此需要定义一个带默认参数的构造函数。定义成员函数print()输出该点。null**null**#include
class CPoint
{
public:
CPoint (double x1=0,double y1=0)
{ x=x1; y=y1; }
void print ()
{
cout<<"("<
class CPoint
{
public:
//带默认参数的构造函数
CPoint (double x1=0,double y1=0)
{ x=x1; y=y1; }
//带一个参数的构造函数
CPoint (double x1)
{ x=x1; y=100; }null**//其他成员函数
private:
double x,y;
};
void main()
{
CPoint point1(10);
//错误,匹配哪个构造函数不确定
point1.print();
}
1.6.4 析构函数 **1.6.4 析构函数 一、析构函数的作用
析构函数是一个特殊类成员函数,它的作用与构造函数相反。析构函数的作用是在对象生存期结束之前自动执行,做清理工作。
例如:
一个类可能在构造函数中分配资源,这些资源要在对象的生存期结束以前释放,释放资源的工作就是自动调用类的析构函数完成的。 1.6.4 析构函数**1.6.4 析构函数二、析构函数的特点
(1)析构函数函数的名字特别,是类名加“~”字符,表明它与构造函数相反;
(2)析构函数没有参数,不能指定返回值类型;
(3)一个类中只能定义一个析构函数,所以析构函数不能重载;
(4)在对象生存期结束时,系统自动调用析构函数。
1.6.4 析构函数**1.6.4 析构函数三、析构函数的定义格式
类名:: ~ 析构函数名()
{
//实现代码
}
例如:Cdate::~Cdate(){ …… } null**【例1.7】 设计一个简单的字符串类CString,类中有两个数据成员,分别表示字符串的长度和字符串的内容。定义一个构造函数和一个析构函数,成员函数GetLength( )返回字符串长度,成员函数GetContents( )获得字符串的内容,重载成员函数SetContents( )可以改变字符串的值。null**null**#include
#include
class CString
{
public:
CString(); //默认构造函数
CString(char *s); //带参数的构造函数
~CString(); //析构函数
int GetLength(); //得到字符串的长度
void GetContents(char *str); //得到字符串的内容
void SetContents(int len, char *cont);//重新设置字符串
void SetContents(char *cont); //重载成员SetContents(),重新设置字符串null**private:
int length; //字符串长度
char *contents; //字符串内容
};
CString::CString()
{
length = 0; contents = NULL;
cout << "字符串对象初始化:默认构造函数" << endl;
}
CString::CString(char *s)
{
length = strlen(s);
contents = new char[length+1];
strcpy(contents,s);
cout << "字符串对象初始化:一个参数的构造函数" << endl;
}
null**CString::~CString()
{
cout << contents << "被销毁" << endl;
if(contents != NULL) delete contents;
}
int CString::GetLength()
{
return length;
}
void CString::GetContents(char *str)
{
strcpy(str, contents);
}null**void CString::SetContents(int len, char *cont)
{
length = len;
if(contents != NULL)
delete contents;
contents = new char[len+1];
strncpy(contents,cont,length);
contents [length]='\0';
cout << "两个参数的SetContents函数" << endl;
}null**void CString::SetContents( char *cont)
{
length = strlen(cont);
if(contents != NULL)
delete contents;
contents = new char[length+1];
strcpy(contents,cont);
cout << "一个参数的SetContents函数" << endl;
}
void main()
{
CString str1,str2;
CString str3("第三个字符串");
str1.SetContents("第一个字符串");
str2.SetContents(20, "第二个字符串两个参数null** int i = str1.GetLength();
char string[100];
str1.GetContents(string);
cout << i << " "<< string << endl;
i = str2.GetLength();
str2.GetContents(string);
cout << i << " " << string << endl;
i = str3.GetLength();
str3.GetContents(string);
cout << i << " " << string << endl;
} 1.6.4 析构函数**1.6.4 析构函数四、构造函数和析构函数的调用时间和调用顺序
构造函数在创建对象时自动调用,调用的顺序是按照对象定义的次序。析构函数的调用顺序正好与构造函数相反。也就是说对于同一存储类别的对象是先构造的对象后析构,后构造的对象先析构。
1.6.5 拷贝构造函数和默认拷贝构造函数 **1.6.5 拷贝构造函数和默认拷贝构造函数 一、拷贝构造函数的作用
用一个已知的对象来初始化另一个对象
二、拷贝构造函数定义格式
类名::拷贝构造函数名(类名 & 引用名)
例如:
Tdate ::Tdate(Tdate & d); //形参是一个对象的引用
CString( const CString & stringSrc ); //形参是一个const的对象引用
1.6.5 拷贝构造函数和默认拷贝构造函数**1.6.5 拷贝构造函数和默认拷贝构造函数三、通常在下述三种情况下,需要用拷贝初始化构造函数:
(1)明确表示由一个对象初始化另一个对象时;如Cdate day3(d1);
(2)当对象作为函数实参传递给函数形参时;如fun(Cdate day);
(3)当对象作为函数的返回值,创建一个临时对象时。
【例1.8】设计一个复数类,两个数据成员分别表示复数的实部(real)和虚部(imag),有2个构造函数分别在不同的情况下初始化对象,函数Print()用于输出复数。**【例1.8】设计一个复数类,两个数据成员分别表示复数的实部(real)和虚部(imag),有2个构造函数分别在不同的情况下初始化对象,函数Print()用于输出复数。#include
class CComplex
{
public:
CComplex(double, double);
CComplex(CComplex &c);
CComplex add(CComplex & x);
void Print();
private:
double real, imag;
}; null**CComplex::CComplex(double r=0.0, double i=0.0)
{
real = r; imag = i;
cout<<"调用两个参数的构造函数"< Print(); //通过指针引用对象成员,输出日期200-1-1
(*dp2).Print(); //通过指针指向的对象引用对象成员,输出日期2006-12-1
……
delete dp1; //调用dp1的析构函数
delete dp2; //调用dp2的析构函数
}
1.7.2 对象数组**1.7.2 对象数组对象数组中的每一个元素都是类的对象
一、声明一维对象数组的语法形式
类名 数组名[常量表达式];
注意:在建立数组时,要调用构造函数初始化每个数组元素。
CStudent s[3] = {CStudent("张三",2006001,19); CStudent("李四",2006002,18); CStudent(“王五”,2006003,20};
二、引用对象数组元素的公有成员:
数组名[下标] . 成员名;null**【例1.10】 设计一个学生类,假设一个班有5个学生,计算这个班学生的平均年龄。#include
#include
class CStudent
{
public:
CStudent(char *na,int i ,int a);
int GetAge();
private:
char name[20];
int id;int age;
}; null**CStudent::CStudent(char *na,int i ,int a)
{
strcpy(name, na);
id=i;
age = a;
}
int CStudent::GetAge()
{
return age;
}
null**void main()
{
int sum=0;
CStudent s[5] = { CStudent("张三",10001, 20), //对象数组初始化
CStudent("李四",10002, 22 ),
CStudent("王五",10003, 24 ),
CStudent("赵六",10004, 26 ),
CStudent(