haihongyuan.com
海量文库 文档专家
当前位置:首页 >> 数学 >>

面向对象程序设计语言C第章深入类与对象-PPT课件_图文

面向对象程序设计语言C第章深入类与对象-PPT课件_图文

面向对象程序设计语言C++
电子科技大学计算机学院

1

第四章 深入类和对象 ? 4.1 构造函数
4.1.1 构造函数的作用

class Date {
int day, month, year; public: void InitDate(int d, int m, int y); //初始化?? … }; 程序员有的时候会忘记了调用初始化函数 或者调用了多次。这都是不好的现象。

2

第四章 深入类和对象 ?4.1 构造函数
4.1.1 构造函数的作用 C++ 为类设计了构造函数 (constructor) 机制,它 可以达到初始化数据成员的目的。 类的构造函数是类的一个特殊成员函数,它没有 返回类型(void也不行),可以有参数,函数名和 类名一样。 当创建类的一个新对象时,自动调用构造函数, 完成初始化工作(需要注意构造函数是否有参数, 以及参数的个数、类型)。

3

第四章 深入类和对象 ? 4.1 构造函数
4.1.1 构造函数的作用 构造函数的作用为: (1) 分配一个对象的数据成员的存储空间; (2) (该功能由系统自动完成。) (2) 执行构造函数(体),一般是初始化一个对象的 部分或全体数据成员。

4

第四章 深入类和对象 ? 4.1 构造函数
4.1.2 构造函数的定义 1. 构造函数的定义

class Date { private: int year, month, day; public: Date(int y, int m, int d); … //其它成员 }; 构造函数的参数可以是缺省的。
5

第四章 深入类和对象 ? 4.1 构造函数
4.1.2 构造函数的定义 2. 构造函数的初始化方式 构造函数有两种方式初始化数据成员: 1)在构造函数体内用赋值语句的方式;
Date::Date(int y, int m, int d) { year = y; month = m; day = d; }

6

第四章 深入类和对象 ?4.1 构造函数
4.1.2 构造函数的定义 2)构造函数的初始化列表的方式

Date::Date(int y,int m,int d):year(y), month(m),day(d) {}

7

第四章 深入类和对象 ? 4.1 构造函数
4.1.2 构造函数的定义 3. 缺省的构造函数 用户定义的类类型中,可以没有构造函数。 编译器会自动给该类类型生成一个没有参数的构 造函数,该函数不作任何初始化工作。 这种构造函数称为缺省的构造函数。 注意: 一个类如果有显式定义的构造函数,编译 器就不会生成缺省构造函数了。

8

第四章 深入类和对象 ? 4.1 构造函数
4.1.2 构造函数的定义 4. 定义类对象时给构造函数提供参数 1)仅仅只有一个参数: 类名 对象名 = 参数; 2)有一个或多个参数: 类名 对象名(参数列表);

9

第四章 深入类和对象 ? 4.1 构造函数
4.1.3 重载构造函数 一个类可以提供多个构造函数,用于在不同场合 进行类对象的初始化工作。 构造函数的重载,它们的参数表必须互不相同。
class Date { int year, month, day; public: Date(int d, int m, int y); Date(int d, int m); Date(int d); Date(); Date(const char * dateStr); … };
10

则下述定义对象的方式都是正确的。 Date today(8, 10, 2019); Date day1(9, 5); Date day2(8); Date Christmas(“Dec 25, 2019”); Date now;

第四章 深入类和对象 ? 4.2 析构函数
与构造函数对应的是析构函数。C++通过析构函 数来处理对象的善后工作。 析构函数没有返回类型,没有参数,函数名是类 名前加“~”。 析构函数的作用为: (1) 执行析构函数(一般没有具体的工作); (2) 释放对象的存储空间。(该功能由系统自动 完成。) 注意:析构函数需要负责释放new申请的空间。

12

第四章 深入类和对象 ? 4.2 析构函数
可以使用完全限定名方式显式地调用析构函 数;若没有显式调用,则在一个对象的作用域 结束时,系统自动调用析构函数。 系统自动调用构造函数和自动调用析构函数的 顺序是相反的。
class X { public: X() { } ~X() { } }; (ex4-8.cpp)

13

考虑一个数组,数组的大小在定义时初始 化,而且其大小在运行时可以改变。 class Array{ int * p; int size; public: Array(int num) { size=num; p=new int[size]; } ~Array( ) {delete [] p; } ...};

第四章 深入类和对象 ?4.3 拷贝构造函数
1. 什么是拷贝构造函数 构造函数的参数可以是任何类型参数,甚至可以 将自己类对象的(常量)引用作为参数,称它为拷 贝构造函数。 拷贝构造函数有两个含义: 首先,它是一个构造函数,当创建一个新对象 时,系统自动调用它; 其次,它将一个已经定义过的对象(参数代表 的对象)的数据成员逐一对应地拷贝给新对象。

15

第四章 深入类和对象 ? 4.3 拷贝构造函数
class A { int x;int y; public: A(int,int); //一般构造函数 A(const A & objA) // 拷贝构造函数 { x=objA.x;y=objA.y;} … }; … // 成员函数的实现

16

void main( ) { A obj1(10,20); //调用A(int,int) A obj2(obj1); //调用A(const A &); A obj3 = obj2; //调用A(const A &); … }

第四章 深入类和对象 ?4.3 拷贝构造函数
1. 什么是拷贝构造函数 如果一个类没有显式定义拷贝构造函数,C++编 译器可以为该类产生一个缺省的拷贝构造函数。 缺省的拷贝构造函数,也将拷贝对象的各个数据 成员拷贝给被拷贝对象的各个数据成员。 这样一来,两个对象的内存映像是一模一样的。

18

第四章 深入类和对象 ? 4.3 拷贝构造函数
2. 拷贝构造函数的作用 拷贝构造函数的作用是: ①创建一个新对象,并将一个已存在的对象拷贝 到这个新对象 ②对象本身做参数(将实参对象拷贝给形参对象) ③函数返回对象(拷贝返回的对象给一个临时对象 )
(ex4-9..10.cpp)

19

class string { int length; char * contents; int who; public: string(int id){ length=0;contents=NULL;who=id; } … }; string fun(string arg) { return (arg); } void main( ) { string s = 1; string t=fun(s); }

第四章 深入类和对象 ?4.4 对象的创建、释放和初始化
1. 对象的创建和释放 可以创建不同形式的各类对象: ①命名的自动对象 每次进入该对象的作用域,都调用构造函数; 每次退出该对象的作用域,都调用析构函数。

21

第四章 深入类和对象 ? 4.4 对象的创建、释放和初始化
class Table {…}; void f(int a) { Table aa; //aa的生命期到f函数返回 Table bb; //bb的生命期到f函数返回 if (a>0) { Table cc; //cc的生命期到if语句结束 … } Table dd; //dd的生命期到f函数返回 … }; //若调用函数f(10) 则调用构造函数的顺序是:aa、bb、cc、dd; 调用析构函数的顺序是:cc、dd、bb、aa。
22

第四章 深入类和对象 ? 4.4 对象的创建、释放和初始化
②自由对象(动态对象) 使用new创建对象(实际上调用构造函数), 使用 delete 释放对象(实际上调用析构函数) ; 当delete释放对象后,该对象就不能再被使用 。 如果构造函数有参数,也必须给出实参。 (ex413.cpp)

23

注意
全局对象: 在程序运行前,调用构造函数创建它们 整个程序结束时,调用析构函数释放它们

第四章 深入类和对象 ? 4.4 对象的创建、释放和初始化
2. 对象的初始化 初始化有许多表示法,C++语言允许下述三种表 示方法。 ①C风格的初始值表的方法 ②赋值表达式的方法 ③表达式表的方法
? ? Class_Name Object(…); Class_Name Object = …;

25

第四章 深入类和对象 ?4.5 对象和指针
4.5.1 this指针 C++为所有非静态成员函数提供了一个称为 this 的指针,因此,常常称成员函数拥有this指针。 this是一个隐含的指针,不能被显式声明 它只是一个形参,一个局部变量 在任何一个非静态成员函数里都存在 它局部于某一对象。 this 指针是一个常指针,可以表示为 ( 但不能显 式声明): X * const this;
26

第四章 深入类和对象 ?4.5 对象和指针
4.5.1 this指针 可以使用const说明符将this声明为指向常量的常 指针。 在类中,一个成员函数的原型后跟一个const, 该函数称为 const 成员函数,它的特点是该函数不 能修改this所指的对象的成员。 例 int GetInt() const; this 指针主要用在运算符重载、自引用等场合。
(ex4-15.cpp)

27

class INT { int num; public: void set(int x) { num=x;} void out() {cout<<num<<‘ ’;}

void fun( INT obj) { INT funObj; num=10;obj.num=20; funObj.num=30; out( );obj.out( ); funObj.out(); set(40); obj.set(50); funObj.set(60); out( );obj.out( );funObj.out( );} }; //类定义结束

void main( ) { INT mainObj; mainObj.set(1); mainObj.out( ); mainObj.fun(mainObj); mainObj.out( ); } 则程序输出: 1 10 20 30 40 50 60 40

第四章 深入类和对象 ? 4.5 对象和指针
4.5.2 指向类对象的指针 在C++中,可以直接使用对象,也可通过指向对 象的指针变量来使用对象。 说明指向对象的指针变量的语法与说明指向其他 任何数据类型的指针变量的语法相同。 One_Class_Name * obj_pointer; 可以通过 指向对象的指针变量->类的公有成员 的方式在类外访问一个类的成员。(ex4-16.cpp)
31

第四章 深入类和对象 ?4.5 对象和指针
4.5.3 指向类的成员的指针 C++ 语言中,可以说明指向类的数据成员的指 针和指向类的成员函数的指针。 这两种指针必须与对象或指向对象的指针结合 使用。

32

第四章 深入类和对象 ? 4.5 对象和指针
4.5.3 指向类的成员的指针 1. 指向类的数据成员的指针 指向类的数据成员的指针定义格式为: 类型名 类名::*指针; 这种说明不是说指针是属于类的 而是说明指针只能指向指定类的指定类型的成员
(ex4-17.cpp)

33

第四章 深入类和对象 ? 4.5 对象和指针
4.5.3 指向类的成员的指针 2. 指向类的成员函数的指针 指向类的成员函数的指针定义的格式为: 类型名 (类名::*指针)(参数表); 函数指针并不属于类, 而是只能指向类的指定原型的函数。(ex4-18.cpp)

34

第四章 深入类和对象 ?4.6 友元关系
一个对象的私有数据,只能通过成员函数进行 访问,这是一堵不透明的墙。 这种限制性的用法给两个类必须共享同一函数 的情形带来了较大的开销。 出于效率(而非技术上必须)考虑, C++ 提供 了一种辅助手段,允许外面的类或函数去访问一个 类的私有数据。

35

class INTEGER { private: int num; public: void Set(int n){ num=n;} }; void Print( INTEGER obj) { cout<<obj.num;} // 错误 why? void mian( ) { INTEGER INTobj; INTobj.set(100); Print(INTobj); …}

class INTEGER { private: int num; public: void Set(int n){ num=n;} friend void Print(INTEGER ); }; void Print( INTEGER obj) { cout<<obj.num;} // ok void mian( ) { INTEGER INTobj; INTobj.set(100); Print(INTobj); …}

类X的友元可以是一个函数(或是一个类) 它不是X的成员函数, 但能访问X的私有成员和保护段的成员, 除了具有这一访问权限外 F在作用域、声明和定义等方面都是一普 通的函数(或类)。

第四章 深入类和对象 ?4.6 友元关系
4.6.1 友元函数 友元函数不属于类,友元函数没有this 指针 这是友元函数与成员函数的主要区别。 友元函数的声明可以放在类的任何段里。

39

第四章 深入类和对象 ?4.6 友元关系
4.6.2 友元类
class Printer; class INTEGER { private: int num1; friend Printer; … };

类Printer的成员函数全都是类INTEGER的友元 函数; 可以访问类INTEGER的任何成员。

40

第四章 深入类和对象 ?4.6 友元关系
4.6.3 友元关系的总结 友元具有如下的特性: 非传递性。即A是B的友元,B是C的友元,但A 不一定是C的友元(除非将A声明 为C的友元); 非对称性。即 A 是 B 的友元,但 B 不一定是 A 的 友元(除非将B声明为A的友元)。

41

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.1 对象数组 可以像创建任何其他数据类型的数组一样的方式 来创建一个类的对象数组。 创建对象数组,每个数组元素都是一个对象,所 以需要多次调用构造函数;释放对象数组,也需要 多次调用析构函数。 One_Class_Name obj_array[10];

42

第四章 深入类和对象 ? 4.7 与类和对象相关的问题
4.7.1 对象数组 C++语言不允许初始化对象数组,所以,要创 建一个类的对象数组,该类的构造函数必须满 足下列三个条件之一: ①没有构造函数; ②有构造函数,但要有一个构造函数不带参数; ③有构造函数,但要有一个构造函数具有的参数 全是缺省参数。(ex4-22.cpp)

43

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.2 类类型做参数类型 由于类是一个数据类型,也可以将对象作为参 数传递给函数 参数传递遵循传值(或传地址)的方式,这同 所有其他的数据类型是相同的。 类类型做形参类型,一般有3种方式: ①对象本身做参数(传值) ②对象引用做参数(传地址) ③对象指针做参数(传值)(ex4-23.cpp)
44

class OBJ { int num; public: void set_num(int x) {num=x;} void out_num( ) { cout<<num<<‘ ’;} };

void fun(OBJ objx) { objx.out_num( ); objx.set_num(100); objx.out_num( ); } void main( ) { OBJ obj; obj.set_num(10); fun(obj); obj.out_num( ); } 结果为 10 100 10

void fun(OBJ & objx) { objx.out_num( ); objx.set_num(100); objx.out_num( ); } void main( ) { OBJ obj; obj.set_num(10); fun(obj); obj.out_num( ); } 结果为 10 100 100

void fun(OBJ * objp) { objp->out_num( ); objp-> set_num(100); objp-> out_num( ); } void main( ) { OBJ obj; obj.set_num(10); fun(&obj); obj.out_num( ); } 结果为 10 100 100

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.2 类类型做参数类型 对象本身做参数,对形参的任何修改都不影响 用作实参的对象; 对象引用做参数,对形参的任何修改就是对实参 的对象的修改; 对象指针做参数,对它指向的对象作任何修改就 是对实参对象的修改。

49

第四章 深入类和对象 ? 4.7 与类和对象相关的问题
4.7.3 类类型常量 类类型常量的基本形式为: 类名(常量表)(ex4-24.cpp)

50

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.4 一个类的对象作为另一个类的成员 一个类的对象可以作为另一个类的数据成员, 简称为对象作为成员,实现的是整体和部分之间 的关系(a part of),即对象的包含关系,有时也称 为类之间的“复合”(composition)。 可以通过 外部对象名.内部对象名.内部对象公有成员 的方式来直接访问内部对象的公有成员。
(ex4-25.cpp)

51

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.4 一个类的对象作为另一个类的成员 一个复合类在构造的时候,首先调用的是内部类 对象的构造函数。 如果内部类对象多于一个,则它们的构造函数 的调用顺序依照它们的定义顺序。 此后,再调用外部类的构造函数。 而在复合类对象析构的时候,析构函数的调用 顺序正好与构造是相反。

52

考虑点和圆的关系。圆的属性包括圆心和半 径,而圆心就是一个点。 class Point { int x, y; public: Point(int x1,int y1) { x=x1; y=y1 ; } void setxy(…); …};

class Circle { float r; Point circle_point; public: Circle(float r1,int a,int b) :circle_point(a,b) {r=r1;} void set_point(…) { circle_point.setxy(…);} … }; void main() {Circle cobj(5.6,1,2);cobj. set_point(…);…}

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.5 非局部环境 在函数体外定义的变量(如全局变量、类中的 静态成员等)称为非局部变量。 它们在main函数执行前进行初始化(如果是对 象,则在 main 函数执行前调用构造函数 ),在 main函数执行结束时才释放对应的存储空间。

55

第四章 深入类和对象 ?4.7 与类和对象相关的问题
4.7.6 临时对象

当函数的返回类型为类类型时,将调用 拷贝构造函数将返回的对象保存到那个临时 对象中。 另外,在类对象的运算中,也可能会产生 临时对象。 临时对象也可以由显示构造函数的调用来 创建。

56

? 利用链表构造一个堆栈类Stack, ? 定义在链表上的最小操作为 插入(push)—在链表前面增加一个项 获取(pop)—获取链表第一个项,并将之删除 清除(clear)—在链表中删除所有的项

class list; class node { friend list; node * next; int item; }; class list { node * head; //链表的头指针 int node_num; //链表中节点的个数 public: list( ){ head=0;node_num=0;}

void push(int numb); int pop( ); void clear( ); ~list( ) {clear( );} }; void list::push(int numb) { node_num++; node * temp; temp=new node; temp->item=numb; temp->next=head; head=temp; }

int list::pop( ) { if (head==0) { cout<<“ Error: empty list\n”; return -1;} else { node_num--; int numb; node * pnode=head; numb=pnode->item; head=head->next; delete pnode; return numb; } }

void list::clear( ) { node_num=0; while (head) pop( ) ; }


网站首页 | 网站地图
All rights reserved Powered by 海文库 haihongyuan.com
文档资料库内容来自网络,如有侵犯请联系客服。3088529994@qq.com