《C++面向对象程序设计》教案.docx
《《C++面向对象程序设计》教案.docx》由会员分享,可在线阅读,更多相关《《C++面向对象程序设计》教案.docx(31页珍藏版)》请在冰点文库上搜索。
《C++面向对象程序设计》教案
《面向对象程序设计》课程教案
课程编号:
08051230
课程名称:
面向对象程序设计(Object-orientedProgramming)
学时:
72学时,其中理论学时54,上机学时18
学分:
3.5
开课部门:
数学与计算机科学学院
开课教研室:
计算机科学
开课教师:
雷小园
开课学期:
第7学期
授课班级:
04信计
先修课程:
C语言程序设计
考核要求:
考试,平时10%,实验20%,考试70%
使用教材:
《C++面向对象程序设计教程(第2版)》,陈维兴,清华大学出版社,2004年
《C++面向对象程序设计习题解答与实验指导》,陈维兴,清华大学出版社,2004年
教学目的与要求:
《面向对象程序设计》是一门计算机及相关专业的重要的专业基础课。
本课程讲述C++语言面向对象的基本特性,包括类、对象、派生类、继承、运算符重载、多态性、虚函数、函数模板、类模板、输入输出、流类库、文件等,使学生掌握面向对象程序设计的基本概念和基本方法,能运用C++语言进行基本的面向对象程序设计。
教学方法:
采用板书讲解C++程序设计,再加以上机练习C++编程。
第3章类和对象
3.1类与对象的基本概念
3.2构造函数与析构函数
例:
点类Point
classPoint
{
private:
intx,y;
public:
Point(){};
Point(intxx,intyy){x=xx;y=yy;}
Point(Point&p){x=p.x;y=p.y;}
intGetX()const{returnx;}
intGetY()const{returny;}
voidSetXY(intxx,intyy){x=xx;y=yy;}
voidShow();
};
voidPoint:
:
Show()
{cout<<"X:
"<"<}
例:
人类Person
classPerson
{protected:
char*name;
intage;
charsex;
public:
Person(char*n,inta,chars);
Person(){name=0;age=0;sex='';}
Person(Person&p);
~Person(){delete[]name;}
voidSetName(char*n);
voidSetAge(inta){age=a;}
voidSetSex(ints){sex=s;}
char*GetName()const{returnname;}
intGetAge()const{returnage;}
charGetSex()const{returnsex;}
voidShow();
};
#include"person.h"
#include
usingnamespacestd;
Person:
:
Person(char*n,inta,chars)
{name=newchar[strlen(n)+1];
strcpy(name,n);
age=a;
sex=s;
}
Person:
:
Person(Person&p)
{name=newchar[strlen(p.name)+1];
strcpy(name,p.name);
age=p.age;
sex=p.sex;
}
voidPerson:
:
SetName(char*n)
{delete[]name;
name=newchar[strlen(n)+1];
strcpy(name,n);
}
voidPerson:
:
Show()
{
cout<<"Name:
"<"<"<}
3.3对象数组与对象指针
1、对象数组
所谓对象数组是指每一数组元素都是对象的数组。
2、对象指针
声明对象指针的一般语法形式为:
类名*对象指针名。
当用指向对象的指针来访问对象成员时,要用“->”操作符。
3、this指针
C++为成员函数提供了一个名字为this的指针,这个指针称为自引用指针。
每当通过一个对象调用一个成员函数时,系统就自动把这个this指针指向该对象。
因此使用的数据成员就是该对象的数据成员。
3.4向函数传递对象
1、使用对象作为函数参数
2、使用对象指针作为函数参数
3、使用对象引用作为函数参数
3.5静态成员
1、静态数据成员
在一个类中,若将一个数据成员说明为static,这种成员称为静态数据成员。
与一般的数据成员不同,无论建立多少个类的对象,都只有一个静态数据的拷贝。
从而实现了同一个类的不同对象之间的数据共享。
定义静态数据成员的格式如下:
static数据类型数据成员名;
静态数据成员在该类定义之外被初始化。
访问静态数据成员可以通过对象或指针来访问,也可以通过类名:
:
来访问。
2、静态成员函数
定义静态成员函数的格式如下:
static返回类型静态成员函数名(参数表);
与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:
类名:
:
静态成员函数名(实参表)
对象.静态成员函数名(实参表)
对象指针->静态成员函数名(实参表)
例:
点类Point(演示静态成员)
classPoint
{
private:
intx,y;
staticintcount;
public:
Point(intxx=0,intyy=0){x=xx;y=yy;count++;}
Point(Point&p){x=p.x;y=p.y;count++;}
intGetX()const{returnx;}
intGetY()const{returny;}
voidSetXY(intxx,intyy){x=xx;y=yy;}
staticintGetCount(){returncount;}
};
intPoint:
:
count=0;
intmain()
{
Pointa(100,200),b;
cout<cout<:
GetCount();
}
3.6友元
1、友元函数
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员、保护成员和公有成员。
2、友元成员
一个类的成员函数也可以作为另一个类的友元,这种成员函数不仅可以访问自己所在类对象中的所有成员,还可以访问friend声明语句所在类对象中的所有成员。
3、友元类
一个类也可以作为另一个类的友元。
友元关系是单向的,不具有交换性。
若类X是类Y的友元,类Y不一定是类X的友元。
友元关系也不具有传递性。
若类X是类Y的友元,Y是类Z的友元,类X不一定是类Z的友元。
例:
点类Point(演示友元)
classPoint
{
private:
intx,y;
staticintcount;
public:
Point(intxx=0,intyy=0){x=xx;y=yy;}
intGetX()const{returnx;}
intGetY()const{returny;}
voidSetXY(intxx,intyy){x=xx;y=yy;}
frienddoubleDist(Pointp1,Pointp2);
};
frienddoubleDist(Pointp1,Pointp2);
{
doublex,y;
x=p1.x-p2.x;
y=p1.y-p2.y;
returnsqrt(x*x+y*y);
}class
intmain()
{
Pointa(100,200),b(300,400);
cout<<"两点间的距离为:
"<}
3.7类对象作为成员
例:
圆类Circle(包含Point类的写法)
classCircle
{
private:
doubleradius;//半径
Pointcenter;//圆心
public:
Circle(){}
Circle(intx,inty,doubler):
center(x,y)
{SetRadius(r);}
Circle(Pointp,doubler):
center(p)
{SetRadius(r);}
doubleGetRadius()const{returnradius;}
voidSetRadius(doubler){radius=(r>=0?
r:
0);}
voidSetValue(intx,inty,doubler)
{center.SetXY(x,y);SetRadius(r);}
doubleArea();
voidShow();
};
constdoublePI=3.14159;
inlinedoubleCircle:
:
Area()
{
returnPI*radius*radius;
}
voidCircle:
:
Show()
{
cout<<"圆心为:
"
center.Show();
cout<<"半径为:
"<}
3.8常类型
1、const引用
const引用的说明形式如下:
const类型说明符&引用名
2、const对象
const对象的说明形式如下:
const类名对象名[(参数表)];
如:
constDataMybirthday(1980,1,1);
const对象的数据成员值不能被改变,const对象必须进行初始化。
通过const对象只能调用它的const成员函数,而不能调用普通成员函数。
3、const数据成员
const数据成员只能通过构造函数的初始化列表来获得初始值。
4、const成员函数
const成员函数的说明格式如下:
类型说明符函数名(参数表)const;
如:
intGetYear()const{returnyear;}
const成员函数不能更新对象的数据成员,也不能调用对象的普通成员函数。
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
5、引用类型的数据成员
引用类型的数据成员也只能通过构造函数的初始化列表来进行初始化。
例
classTest
{private:
inta;
constintb;//不能写成constintb=10,因类的定义还没分配空间
int&c;//不能写成constint&c=a,因变量a还没分配空间
public:
Test(inti,intj,int&k):
b(j),c(k)
{a=i;}
Test():
b(10),c(a){a=20;}
}
第4章派生类与继承
4.1派生类的概念
4.2派生类的构造函数与析构函数
例:
圆类Circle(继承Point类的写法)
classCircle:
publicPoint
{private:
doubleradius;//半径
public:
Circle(){}
Circle(intx,inty,doubler):
Point(x,y)
{SetRadius(r);}
Circle(Pointp,doubler):
Point(p)
{SetRadius(r);}
doubleGetRadius()const{returnradius;}
voidSetRadius(doubler){radius=(r>=0?
r:
0);}
voidSetValue(intx,inty,doubler)
{SetXY(x,y);SetRadius(r);}
doubleArea();
voidShow();
};
constdoublePI=3.14159;
inlinedoubleCircle:
:
Area()
{returnPI*radius*radius;
}
voidCircle:
:
Show()
{cout<<"圆心为:
"
Point:
:
Show();
cout<<"半径为:
"<}
关于基类和派生类的几点说明
1、派生类继承了它的所有基类中除构造函数和析构函数之外的所有成员。
2、在派生类中成员按访问属性划分为四种:
不可访问的成员、私有成员、保护成员、公有成员。
3、对从基类继承下来的成员初始化工作是通过调用基类的构造函数来完成的,调用方法是在派生类的构造函数中用初始化列表。
4、如果在派生类的构造函数省略了基类的初始化列表,则将调用基类的缺省构造函数。
5、如果基类定义了带有参数的构造函数时,派生类就应当定义构造函数,以便显式地调用基类的构造函数。
6、如果派生类定义了与基类同名的新数据成员或成员函数,则此派生类的成员就覆盖了基类的同名成员,直接使用成员名只能访问到派生类的成员。
7、在同名覆盖的情况下,可以使用基类名+作用域分辨符来访问基类的同名成员。
8、如果派生类和基类的某个成员函数重名,但参数表不同,仍然属于覆盖,不属于重载。
9、对派生类的对象,构造函数的执行过程是:
先调用基类的构造函数(按它们被继承时声明的顺序),再调用内嵌对象成员的构造函数(按内嵌对象声明的顺序),最后执行自己的构造函数体中的内容。
10、析构函数的调用次序正好和构造函数的调用次序相反。
例:
学生类Student
//student.h
#include"person.h"
classStudent:
publicPerson
{
protected:
char*Department;
intNumber;
public:
Student(){Department=0;Number=0;}
Student(char*,int,char,char*,int);
Student(Student&stu);
~Student(){delete[]Department;}
voidSetDep(char*);
voidSetNum(intnum){Number=num;}
char*GetDep()const{returnDepartment;}
intGetNum()const{returnNumber;}
voidShow();
};
//student.cpp
#include"student.h"
#include
usingnamespacestd;
Student:
:
Student(char*name,intage,charsex,char*dep,intnum)
:
Person(name,age,sex)
{Department=newchar[strlen(dep)+1];
strcpy(Department,dep);
Number=num;
}
Student:
:
Student(Student&stu):
Person(stu)
{Department=newchar[strlen(stu.Department)+1];
strcpy(Department,stu.Department);
Number=stu.Number;
}
voidStudent:
:
SetDep(char*dep)
{delete[]Department;
Department=newchar[strlen(dep)+1];
strcpy(Department,dep);
}
voidStudent:
:
Show()
{Person:
:
Show();
cout<<"Department:
"<"<}
4.3调整基类成员在派生类中访问属性的其他方法
4.4多重继承
例1:
X和Y是基类,Z从X和Y派生
classX
{public:
intb;
X(intk){b=k;}
};
classY
{public:
intc;
Y(intk){c=k;}
};
classZ:
publicX,publicY
{public:
intd;
Z(inti,intj,intk):
X(i),Y(j){d=k;}
}
例2:
X和Y都从W派生而来
classW
{public:
inta;
W(intk){d=k;}
};
classX:
publicW
{public:
intb;
X(inti,intk):
W(i){b=k;}
};
classY:
publicW
{public:
intc;
Y(inti,intk):
W(i){c=k;}
};
classZ:
publicX,publicY
{public:
intd;
Z(inti,intj,intk,intl):
X(i,j),Y(i,k){d=l;}
}
intmain()
{Zt(10,20,30,40);
cout<:
a<:
a<}
例3:
将W做为X和Y的虚基类
classW
{public:
inta;
W(intk){a=k;}
};
classX:
virtualpublicW
{public:
intb;
X(inti,intk):
W(i){b=k;}
};
classY:
virtualpublicW
{public:
intc;
Y(inti,intk):
W(i){c=k;}
};
classZ:
publicX,publicY
{public:
intd;
Z(inti,intj,intk,intl):
W(i),X(i,j),Y(i,k){d=l;}
}
intmain()
{Zt(10,20,30,40);
cout<}
在使用虚基类机制时应该注意以下几点:
(1)如果在虚基类中定义有带形参的构造函数,并且没有定义缺省形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调用,以初始化在虚基类中定义的数据成员。
(2)建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。
该派生类的其他基类对虚基类构造函数的调用都自动被忽略。
(3)若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;
(4)对于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下;
(5) 对于非虚基类,构造函数的执行顺序仍是先左后右,自上而下;
(6)若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。
4.5赋值兼容规则
所谓赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代。
附:
线性表——顺序表
classSeqList
{private:
int*data;
intsize;
intMaxSize;
public:
SeqList(intsz=100);
~SeqList(){delete[]data;}
intLength()const{returnsize;}
boolIsEmpty()const{returnsize==0;}
voidInsert(constint&x,intk);
voidDelete(intk);
intGetData(intk)const;
intFind(constint&x)const;
voidShow()const;
};
SeqList:
:
SeqList(intsz)
{MaxSize=sz;
data=newint[MaxSize];
size=0;
}
voidSeqList:
:
Insert(constint&x,intk)
{if(k<1||k>size+1){cerr<<"越界出错";exit
(1);}
if(size==MaxSize){cerr<<"顺序表已满";exit
(1);}
for(inti=size-1;i>=k-1;i--)data[i+1]=data[i];
data[k-1]=x;
size++;
}
voidSeqList:
:
Delete(intk)
{if(size==0){cerr<<"顺序表空";exit
(1);}
if(k<1||k>size){cerr<<"越界出错";exit
(1);}
for(inti=k;isize--;
}
intSeqList:
:
GetData(intk)const
{if(k<1||k>size){cerr<<"越界出错";exit
(1);}
returndata[k-1];
}
intSeqList:
:
Find(constint&x)