第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.

Slides:



Advertisements
Similar presentations
第3-2章 类与 对象 Java类的特性 教学内容: 类的私有成员与公共成员 方法的重载 构造方法 实例成员与静态成员 重点: 重载 难点:
Advertisements

单元二:面向对象程序设计 任务二:借书卡程序设计.
第一章 有理数 一.本章学习目标 1.理解有理数的意义,能用数轴上的点表示有理数,能比较有理数的大小.
3.1.1 随机事件的概率(一).
不会宽容人的人, 是不配受到别人的宽容的。 贝尔奈.
复习回顾 a a×a a×a×a a a×a×a= a×a= 1.如图,边长为a厘米的正方形的面积 为 平方厘米。
四資二甲 第三週作業 物件導向程式設計.
第一章 体育统计的基本知识 主讲教师:王丽艳 徐栋.
第九讲 类与对象 (I)面向对象基础.
第6章 多态性与虚函数.
第四章 时间序列的分析 本章教学目的:①了解从数量方面研究社会经济现象发展变化过程和发展趋势是统计分析的一种重要方法;②掌握时间数列编制的基本要求;③理解和掌握水平速度两方面指标的计算及运用④理解和掌握长期趋势分析和预测的方法。 本章教学重点:现象发展的水平指标和速度指标。 本章教学难点:现象变动的趋势分析。
第三章 控制结构.
大綱: AAA 性質 SAS 性質 SSS 性質 顧震宇 台灣數位學習科技股份有限公司
复习与总结.
資料大樓 --談指標與陣列 綠園.
内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数. 常宝宝 北京大学计算机科学与技术系
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
類別樣板 Class Template 類似函式樣板 由類別樣板產生的類別稱為類別樣版的實體(instance)
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
第四章 小技巧.
C语言 程序设计基础与试验 刘新国、2012年秋.
第3讲 C++程序控制结构 3.1 顺序结构 3.2 分支结构 3.3 循环结构 3.4 转向控制 3.5 综合案例分析.
Ch02-基礎語法.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C/C++/Java 哪些值不是头等程序对象
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
C++大学基础教程 第5章 数组 北京科技大学 信息基础科学系.
C++面向对象程序设计 谭浩强编著 授课教师:姬广永 QQ: 学习网站:
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
第二章 基本数据类型及运算 C数据类型概述 基本数据类型 运算符和表达式 混合运算与类型转换 数据的输入输出 顺序程序设计举例.
第12讲 多继承与虚基类 多继承 虚基类.
C++语言程序设计 第六章 数组 指针与字符串.
C语言复习3----指针.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
保留字與識別字.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
面向对象技术 练习 ffh.
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
Review 1~3.
第九章 物件導向-進階.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板.
第 9 章 建構函式與解構函式.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
挑戰C++程式語言 ──第9章 函數.
#include <iostream.h>
第四章 函数 丘志杰 电子科技大学 计算机学院 软件学院.
方法進階及物件導向基礎 Lecturer: 楊昌樺.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
Class 2005/05/25.
第三章 高级函数特性.
§12-5 同方向同频率两个简谐振动的合成 一. 同方向同频率的简谐振动的合成 1. 分振动 : 2. 合振动 : 解析法
第2章 Java语言基础.
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
變數與資料型態  綠園.
第二章 Java基础语法 北京传智播客教育
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Presentation transcript:

第10讲 构造函数和析构函数 构造函数 析构函数 This 指针

学习目标 理解构造函数和析构函数的作用; 熟练掌握构造函数和析构函数的定义和使用; 掌握构造函数的重载; 理解动态建立对象和对象数组的方法 理解This指针的作用;

第9讲 构造函数和析构函数 构造函数 析构函数 This 指针

构造函数的引入 如何实现对象成员数据的快速初始化? class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; 如何实现对象成员数据的快速初始化? void main(void) { A a1; a1.m=10; a1.n=20; a1.Setxy(2.0 , 5.0); a1.Print(); } 对a1对象的公有 成员数据赋初值 对a1对象的私有 成员数据赋初值

构造函数与析构函数 构造函数和析构函数是在类体中说明的两种特殊的成员函数。 构造函数是在创建对象时,使用给定的值来将对象初始化。

构造函数 构造函数的函数名必须与类名相同。构造 函数的主要作用是完成初始化对象的数据 成员以及其它的初始化工作。 在定义构造函数时,不能指定函数返回值 的类型,也不能指定为void类型。 一个类可以定义若干个构造函数。当定义 多个构造函数时,必须满足函数重载的原 则。

构造函数的定义 class <类名> { public: <类名> (参数表) {函数体} } 构造函数必须 为公有函数 构造函数名与 所属类名相同 构造函数 无返回值 构造函数的函 数体

构造函数 构造函数可以指定参数的缺省值。 若定义的类要说明该类的对象时,构造函 数必须是公有的成员函数。如果定义的类 仅用于派生其它类时,则可将构造函数定 义为保护的成员函数。 由于构造函数属于类的成员函数,它对私 有数据成员、保护的数据成员和公有的数 据成员均能进行初始化。

构造函数 属性 构造函数 目的 类的公有成员函数 实现成员数据初始化 实现 私有数据成员 保护数据成员 公有数据成员

例1: x=2 y=3 x=1 y=2 class A { float x,y; 构造函数, 初始化对象 public: A(float a,float b){ x=a; y=b;}// float Sum(void) { return x+y;} Print(void) { cout<<"x="<<x<<'\t'<<"y="<<y<<endl;} }; void main(void) { A a1(2.0, 3.0); A a2(1.0,2.0); a1.Print(); a2.Print(); } 构造函数, 初始化对象 定义时调用构造 函数进行成员数 据的初始化 x=2 y=3 x=1 y=2

每一个对象必须要有相应的构造函数 class A{ float x,y; public: A(float a, float b=10) { x=a; y=b; } A() { x=0; y=0;} void Print(void) { cout<<x<<'\t'<<y<<endl; } }; void main(void) { A a1, a2(20.0), a3(3.0, 7.0); a1.Print(); a2.Print(); a3.Print(); } 带缺省参数的构造函数 不带参数的构造函数 0 0 20 10 3 7 每一个对象必须要有相应的构造函数

若没有显式定义构造函数,系统默认缺省的构造函数。 每一个对象必须要有相应的构造函数 若没有显式定义构造函数,系统默认缺省的构造函数。 class A{ float x,y; public: A() {} void Print(void) { cout<<x<<'\t'<<y<<endl; } }; 对象开辟了空间,但没有初始化 隐含的缺省的构造函数 只允许这样定义对象 A a1, a2;

构造函数 关于缺省的构造函数,说明以下几点: 在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数 所有的对象在定义时,必须调用构造函数 不存在没有构造函数的对象! 缺省的构造函数并不对所产生对象的数据成员赋初值;即新产生对象的数据成员的值是不确定的。

构造函数的特点 存在性 唯一性 每一个对象必须要有与之对应的构造函数 所有的对象在定义时,必须调用构造函数 不存在没有构造函数的对象 任一对象调用的构造函数必须唯一

构造函数的特点 缺省的构造函数 若无显式定义构造函数,系统产生默认缺省构造函数 若已显式定义构造函数,系统不产生默认缺省构造函数 1.不带参数的构造函数 A( ){ } 缺省的构造函数 2.带默认缺省值的构造函数 A(int a=3 ){ }

例3: class A { float x,y; public: void Print(void) cout<<x<<'\t'<<y<<endl; } }; 系统生成的隐含缺省的构造函数 无显式定义的构造函数 A() { } 缺省构造函数时只许这样定义对象 A a1, a2;

例4: class A { float x,y; public: A(float a=0,float b=5) { x=a;y=b; } void Print(void) { cout<<x<<'\t'<<y<<endl;} }; void main(void) { A a1; A a2(3.0,30.0); } 显式定义了构造函数 不产生隐含的构造函数A() { } 含有默认参数值 调用默认参数的构造函数 调用含参数的构造函数

class A{ float x,y; public: A(){ }//缺省的构造函数,编译器自动产生,可以不写 float Sum(void) { return x+y; } void Set(float a,float b) { x=a; y=b;} void Print(void) { cout<<"x="<<x<<'\t'<<"y="<<y<<endl; } }; void main(void) { A a1,a2;//产生对象时,自动调用缺省的构造函数,不赋值 a1.Set (2.0,4.0); cout<<"a1: "; a1.Print (); cout<<"a1.sum="<<a1.Sum ()<<endl; a2.Print();//打印随机值 }

显式定义了构造函数,不产生缺省的构造函数 class A{ float x,y; public: A(float a,float b) { x=a; y=b; } void Print(void){ cout<<x<<'\t'<<y<<endl; } }; void main(void) { A a1; A a2(3.0,30.0); } 显式定义了构造函数,不产生缺省的构造函数 error,定义时,没有构造函数可供调用

构造函数 在类中,若定义了没有参数的构造函数,或各参 数均有缺省值的构造函数也称为缺省的构造函数 ,缺省的构造函数只能有一个。 在类中,若定义了没有参数的构造函数,或各参 数均有缺省值的构造函数也称为缺省的构造函数 ,缺省的构造函数只能有一个。 产生对象时,系统必定要调用构造函数。所以任 一对象的构造函数必须唯一。

举例 class A{ float x,y; public: A(float a=10,float b=20){ x=a; y=b; } void Print(void){ cout<<x<<'\t'<<y<<endl; } }; void main(void) { A a1; A a2(3.0,30.0); } 两个函数均为缺省的构造函数 两个构造函数均可供调用,构造函数不唯一

1 2 3 4 动态建立对象时的构造函数 用new建立类的对象时,可以 使用参数初始化动态空间。 使用new运算符来动态地建立对象 STEP 1 STEP 2 STEP 3 STEP 4 使用new运算符来动态地建立对象 自动调用构造函数,完成初始化对象的数据成员 返回动态对象的起始地址 不再使用动态对象时,用delete运算符来释放对象所占用的存储空间 用new建立类的对象时,可以 使用参数初始化动态空间。

用new动态开辟空间,调用构造函数初始化 class A{ float x,y; public: A(float a, float b){ x=a;y=b; } A(){ x=0; y=0; } void Print(void){ cout<<x<<'\t'<<y<<endl; } }; void main(void) { A *pa1,*pa2; pa1=new A(3.0, 5.0); pa2=new A; pa1->Print(); pa2->Print(); delete pa1; //用delete释放空间 delete pa2; //用delete释放空间 } 输出:3 5 0 0 用new动态开辟对象空间,初始化 用new动态开辟空间,调用构造函数初始化

第9讲 构造函数和析构函数 构造函数 析构函数 This 指针

析构函数 析构函数的作用与构造函数正好相反,是在对象的生命 期结束时,释放系统为对象所分配的空间,即要撤消 一个对象。 析构函数也是类的成员函数,类体外定义析构函数的格 式为: ClassName::~ClassName( ) { ...... // 函数体; }

析构函数的特点如下: 析构函数是成员函数,函数体可写在类体内,也可写在类体外。 析构函数是一个特殊的成员函数,函数名必须与类名相同,并 在其前面加上字符“~”,以便和构造函数名相区别。 析构函数不能带有任何参数,不能有返回值,不指定函数类型。 一个类中,只能定义一个析构函数,析构函数不允许重载。 析构函数是在撤消对象时由系统自动调用的。 在程序的执行过程中,当遇到某一对象的生存期结束时,系统 自动调用析构函数,然后再收回为对象分配的存储空间。

析构函数调用 调用缺省的构造函数 调用非缺省的构造函数 退出主函数 调用析构函数 调用析构函数 class A{ float x,y; public: A(float a,float b) { x=a;y=b;cout<<"调用非缺省的构造函数\n";} A() { x=0; y=0; cout<<"调用缺省的构造函数\n" ;} ~A() { cout<<"调用析构函数\n";} void Print(void) { cout<<x<<'\t'<<y<<endl; } }; void main(void) { A a1; A a2(3.0,30.0); cout<<"退出主函数\n"; } 调用缺省的构造函数 调用非缺省的构造函数 退出主函数 调用析构函数 调用析构函数

动态开辟内存的情况 在撤消对象时,系统自动收回为对象所分配 的存储空间,而不能自动收回由new分配的动 态存储空间。 在程序的执行过程中,对象如果用new运算符 开辟了空间,则在类中应该定义一个析构函 数,并在析构函数中使用delete删除由new分 配的内存空间。

{ if(string){ Length=strlen(string); Sp=new char[Length+1]; class Str{ char *Sp; int Length; public: Str(char *string) { if(string){ Length=strlen(string); Sp=new char[Length+1]; strcpy(Sp,string); } else Sp=0; void Show(void){ cout<<Sp<<endl; } ~Str() { if(Sp) delete Sp; } }; void main(void) { Str s1("Study C++"); s1.Show(); 在构造函数中将成员数据指针指向动态开辟的内存 用初值为开辟的内存赋值 析构函数,当释放对象时收回用new开辟的空间

用new开辟空间 ‘S’ ‘t’ ‘u’ ‘d’ ‘y’ ‘ ’ ‘C’ ‘+’ ‘\0’ ‘S’ ‘t’ ‘u’ ‘d’ ‘y’ ‘ ’ string ‘S’ ‘t’ ‘u’ ‘d’ ‘y’ ‘ ’ ‘C’ ‘+’ ‘\0’ new开辟的空间 Sp Length=strlen(string); strcpy(Sp,string); Sp=new char[Length+1];

不同存储类型的对象调用构造函数及析构函数 对于全局定义的对象(在函数外定义的对象), 在程序开始执行时,调用构造函数;到程序结束 时,调用析构函数。 对于局部定义的对象(在函数内定义的对象), 当程序执行到定义对象的地方时,调用构造函数 ;在退出对象的作用域时,调用析构函数。 用static定义的局部对象,在首次到达对象的定义 时调用构造函数;到文件结束时,调用析构函数

class A{ float x,y; public: A(float a, float b){x=a;y=b;cout<<"初始化自动局部对象\n";} A(){ x=0; y=0; cout<<"初始化静态局部对象\n";} A(float a){ x=a; y=0; cout<<"初始化全局对象\n"; } ~A(){ cout<<“调用析构函数”<<endl; } }; A a0(100.0);//定义全局对象 void f(void) { cout<<" 进入f()函数\n"; A ab(10.0, 20.0);//定义局部自动对象 static A a3; //初始化局部静态对象 } void main(void) { cout<<"进入main函数\n"; f(); f(); } 初始化全局对象 进入main函数 进入f()函数 初始化自动局部对象 初始化静态局部对象 调用析构函数 进入f()函数 初始化自动局部对象 调用析构函数 调用析构函数 调用析构函数

动态构造及析构对象数组 用new运算符来动态生成对象数组时,自动调 用构造函数,而用delete运算符来释放p1所指 向的对象数组占用的存储空间时,在指针变量 的前面必须加上[ ], 才能将数组元素所占用的 空间全部释放。否则,只释放第0个元素所占 用的空间。 pa1=new A[3]; ..... delete [ ]pa1;

class A{ float x,y; public: A(float a=0, float b=0){x=a; y=b;cout<<"调用了构造函数\n";} void Print(void){ cout<<x<<'\t'<<y<<endl; } ~A() { cout<<"调用了析构函数\n"; } }; void main(void) { cout<<"进入main()函数\n"; A *pa1; pa1=new A[3];//开辟数组空间 cout<<"\n完成开辟数组空间\n\n"; delete [ ]pa1; //必须用[]删除开辟的空间 cout<<"退出main()函数\n"; } 进入main()函数 调用了构造函数 调用了构造函数 调用了构造函数 完成开辟数组空间 调用了析构函数 调用了析构函数 调用了析构函数 退出main()函数

缺省的析构函数 ClassName::~ClassName() { }; 若在类的定义中没有显式地定义析构函数时, 则编译器自动地产生一个缺省的析构函数,其 格式为: ClassName::~ClassName() { }; 任何对象都必须有构造函数和析构函数,但在 撤消对象时,要释放对象的数据成员用new运 算符分配的动态空间时,必须显式地定义析构 函数。

完成拷贝功能的构造函数 完成拷贝功能的构造函数的一般格式为: 可以在定义一个对象的时候用另一个对象为其初 始化,即构造函数的参数是另一个对象的引用, 这种构造函数常为完成拷贝功能的构造函数。 完成拷贝功能的构造函数的一般格式为: ClassName::ClassName(ClassName &<变量名>) { ...... // 函数体完成对应数据成员的赋值 }

A(float a=0, float b=0){x=a; y=b;} A(A &a) { x=a.x; y=a.y;} }; class A{ float x,y; public: A(float a=0, float b=0){x=a; y=b;} A(A &a) { x=a.x; y=a.y;} }; 形参必须是同类型对象的引用 void main(void) { A a1(1.0,2.0); A a2(a1); } 实参是同类型的对象

class A{ float x,y; public: A(float a=0, float b=0){x=a; y=b; cout<<"调用了构造函数\n";} A(A &a) { x=a.x;y=a.y;cout<<“调用了完成拷贝功能的构造函数\n”; } void Print(void){ cout<<x<<'\t'<<y<<endl; } ~A() { cout<<"调用了析构函数\n"; } }; void main(void) { A a1(1.0,2.0); A a2(a1); a1.Print(); a2.Print(); } 用已有的对象中的数据为新创建的对象赋值 调用了构造函数 调用了完成拷贝功能的构造函数 1 2 调用了析构函数

隐含的完成拷贝的构造函数 如果没有定义完成拷贝功能的构造函数,编译器自动生成一个隐含的完成拷贝功能的构造函数,依次完成类中对应数据成员的拷贝。 隐含的构造函数 A::A(A &a) { x=a.x; y=a.y; }

class A{ float x,y; public: A(float a=0, float b=0){x=a; y=b;cout<<"调用了构造函数 \n";} void Print(void){ cout<<x<<'\t'<<y<<endl; } ~A() { cout<<"调用了析构函数\n"; } }; void main(void) { A a1(1.0,2.0); A a2(a1); A a3=a1; a1.Print(); a2.Print(); a3.Print(); } 调用了构造函数 1 2 调用了析构函数

第9讲 构造函数和析构函数 构造函数 析构函数 This指针

This 指针 用类定义一个对象时,由系统自动建立指 向该对象的指针称为this指针。 this指针的默认定义格式: <类名> * const this

This 指针 this是每个成员函数的一个隐含参数,通过该 参数,成员函数可以获知当前对象的地址,从 而可以操纵对象所拥有的数据成员。

This 指针 this作用域是在类内部,编译器会自动将对象 本身的地址作为一个隐含参数传递给函数。也 就是说,即使你没有写上this指针,编译器在 编译的时候也是加上this的。对各成员的访问 均通过this进行。 例如,调用date.SetMonth(9) <===> SetMonth(&date, 9),this帮助完成了这一转换 .

This 指针 当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。 #include<iostream.h>   class Point   {   int x, y;   public:   Point(int a, int b) { x=a; y=b;}   void MovePoint( int a, int b){ x+=a; y+=b;}   void print(){ cout<<"x="<<x<<"y="<<y<<endl;}   };   void main( )   Point point1( 10,10);   point1.MovePoint(2,2);   point1.print( );   } 当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。

This指针 int Box::volume() { return(height * width * length);} C++把它处理成 int Box::volume (Box * this) { return (this->height * this->width * this->length)} 即在成员函数的形参列表中增加一个this指针。在调 用该成员函数时,实际上是用以下方式调用: a.volume (&a);

第9讲 构造函数和析构函数 构造函数 析构函数 This指针

作业题: “完数”,是指一个数恰好等于它的因子之和。如 6=1+2+3,6是完数;28=1+2+4+7+14,28是完数。 目前已找到48 个完数。 未解之谜:共 有多少完数? 毕达哥拉斯 尼克马修斯 设计构造函数,编程求10000之内的所有“完数”

作业题: 要求: 用面向对象的方法编程,即尽可能地将变量和运算 放在类中完成,主函数应保持最简。 要用到构造函数和析构函数 用面向对象的方法编程,即尽可能地将变量和运算 放在类中完成,主函数应保持最简。 要用到构造函数和析构函数 要将全部完数的加法表达式(如6=1+2+3 )输出在 屏幕上,每行仅输出一式。