内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数. 常宝宝 北京大学计算机科学与技术系

Slides:



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

四資二甲 第三週作業 物件導向程式設計.
程序设计实习 3月份练习解答
面向对象的程序设计(一).
第三章 鏈結串列 Linked List.
设计模式可以帮助我们改善系统的设计,增强 系统的健壮性、可扩展性,为以后铺平道路。
資料結構與C++程式設計進階 資料結構概論 講師:林業峻 CSIE, NTU 6/ 7, 2010.
第15章 繼承與多重繼承 15-1 繼承的基礎 15-2 覆寫與隱藏父類別的成員 15-3 子類別的建構與解構子 15-4 多重繼承
第八章 类和对象.
C程序设计 第9章 自定义数据类型 主讲教师: 鲁 萍 西安建筑科技大学 理学院.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
資料大樓 --談指標與陣列 綠園.
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
第十一章 面向对象设计 第十二章 面向对象实现
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
结构体和共用体 2 梁春燕 华电信息管理教研室.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
Ch10 類別與物件-方法 Java程式設計(2).
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
第3章 继承和派生.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
西安交通大学 计算机教学实验中心 大学C++程序设计教程 西安交通大学 计算机教学实验中心
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
第三章 C++中的C 面向对象程序设计(C++).
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
第四章 小技巧.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第13章 结构体的应用 13.1 了解由用户构造的数据类型 13.2 结构体类型说明及结构体变量 13.3 结构体数组
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
第4讲 C++程序控制结构(二) 4.1 循环结构 4.2 转向控制 4.3 综合案例分析.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
辅导课程八.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
第十章 用户自定义数据类型 目录 学生信息管理系统的开发 结构体数据类型的概述 结构体变量的使用 结构体数组
OOP6 結構Struct 黃兆武.
第十章 结构体与链表 西安工程大学.
第三课 标识符、关键字、数据类型.
C#程序设计基础 $3 成员、变量和常量.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.
特定消耗品說明 (指碳粉匣、墨水匣) 國立清華大學 保管組製作.
C++复习2----类与对象.
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
Object-Oriented Programming in C++ 第二章 类和对象
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
第九章 物件導向-進階.
目标 流程控制 字符串处理 C# 的类和对象 C# 访问修饰符 C# 构造函数和析构函数.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
JAVA 程式設計與資料結構 第三章 物件的設計.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
本节内容 在堆中创建对象 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
题目详细要求、参考资料及更新发布于: 第二周 链表与指针 题目详细要求、参考资料及更新发布于:
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
第十二章 C与C C转入C++时不需改变的内容 12.2 C转入C++的一些与类无关的 新特性
Presentation transcript:

常宝宝 北京大学计算机科学与技术系 chbb@pku.edu.cn

内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数

对象的生命周期 在面向对象的程序设计环境中,整型、实型以及其它其他基本数据类型的变量均可以被视作为特殊对象。 和C语言中变量的类型一样,C++中对象可分作三类。 全局对象 存在于全局数据区,编译程序负责为其分配存储空间和回收空间。主函数运行前,其存储分配及初始化工作已经完成,主函数运行结束后,其所占存储空间才被回收。 局部对象 存在于栈区,编译程序负责为其分配存储空间和回收空间,程序运行进入其作用域后为其分配存储空间,在离开其作用域时,其所占空间被回收。 动态分配的对象 存在于堆区,程序员在需要时为其分配存储空间并在不需要时回收其所占用的存储空间。

对象的生命周期 TDate gd; //定义全局对象 int main() { TDate ld; //定义局部对象 TDate *pd, *pd2; ... pd = new TDate;//建立动态分配的对象 delete pd;//动态分配对象所占空间被回收 pd2 = func(); delete pd2; //局部对象所占空间被回收 } //程序运行结束时,全局对象所占据空间被回收 TDate* func() { TDate ld1; TDate *pd1; ... pd1 = new TDate; return pd1; } //动态对象未回收

对象的生命周期 对象从“诞生”到“死亡”通常经历下面的阶段: 对象创建 根据对象类型为对象分配所存储空间。 对象初始化 给整型、实型、字符型、指针变量赋初值;给对象的数据成员赋初值 对象的使用 在程序中存取该对象 对象的善后清理 对象被回收前必要的善后处理,基本类型对象无需此步骤 回收对象所占空间

初始化 C++中,初始化基本数据类型变量的规则和C语言相同。 int a=6;//变量初始化 int b; //是否初始化依赖于变量是全局的还是局部的 int* pc = new int; //新分配的整型变量不作初始化 C++中,结构变量初始化可以按照C语言中结构变量初始化的方式进行。 struct student { char* name; char* id; float score; }; …… student s1 = {“王二”, “02201009”, 89.3};

初始化 由于类的数据成员有不同的存取权限,因而不能象初始化结构那样对类对象进行初始化。 class TDate { public: void Set( int, int, int ); int IsLeapYear(); void Print(); private: int year; int month; int day; }; //下面初始化对象的方式是错误//的,因为客户存取了对象的私//有数据成员。C++中把数据成员//设为私有成员,为的是向客户//隐层对象内部的实现细节 TDate d1 = {2003, 9, 27};

初始化 可以在定义类的时侯,专门定义初始化对象的成员函数,负责对象的初始化工作。例如: void TDate::Set( int m, int d, int y ) { month = m; day = d; year = y; } 但是这样程序员需要在每次创建对象后调用用于初始化的成员函数。例如: TDate d; d.Set(9,27,2003); TDate *pd = new TDate; pd->Set(9,29,2003); 面向对象的程序设计思想认为对象的初始化工作应由编译程序负责处理,应该在对象创建后立即进行初始化。

构造函数 C++要求为每个类定义构造函数,负责该类对象的初始化工作。 构造函数是类的一个特殊成员函数。函数名和类名相同,且没有返回值,可以在类的内部进行定义,也可以在外部进行定义。 class TDate { public: TDate(); int IsLeapYear(); void Print(); private: int year; int month; int day; }; TDate::TDate() { year = 2000; month = 1; day = 1; } void TDate::TDate() { year = 2000; month = 1; day = 1; }

构造函数 构造函数在对象创建时自动被调用。程序员不能在程序中调用构造函数。 TDate gd; //构造函数TDate()被自动调用 int main() { TDate ld; //构造函数TDate()被自动调用 ... TDate *pd = new TDate; //构造函数TDate()被自动调用 delete pd; }

构造函数的重载 构造函数可以重载以满足不同的对象初始化需求。(即为一个类定义多个构造函数,但各个构造函数带不同类型或数量的参数) class TDate { public: TDate(); TDate(int, int, int ); int IsLeapYear(); void Print(); private: int year; int month; int day; }; TDate::TDate() { year = 2000; month = 1; day = 1; } TDate::TDate(int m, int d, int y) { year = y; month = m; day = d;

构造函数的重载 如果定义了有参数的构造函数,则定义对象时,可以给定参数。编译程序根据重载函数的规则决定应该调用哪个构造函数。例如: TDate d1(9,27,2003);//调用构造函数TDate(int, int, int) TDate d2;//调用构造函数TDate() TDate d3(2003);//错误,编译程序找不到只有一个参数的构造函数 程序员不能直接显式调用构造函数。 TDate d2; d2.TDate(9,27,2003);//错误,编译出错

构造函数的重载 可以为构造函数的参数指定默认值。但应注意重载和默认值有可能发生冲突。 TDate::TDate(int m, int d = 1, int y = 2000) { year = y; month = m; day = d; } ... TDate d(5);//调用构造函数Tdate(5,1,2000)

默认构造函数 每个类必须有构造函数。没有构造函数,对象不能被创建。 如果程序员没有提供任何构造函数,编译程序将自动创建一个没有参数的构造函数,该构造函数即为默认构造函数。 默认构造函数不作任何初始化工作。其函数体为空。 class TDate { public: void Set( int, int, int ); int IsLeapYear(); void Print(); private: int year; int month; int day; }; TDate::TDate() {//默认构造函数 }

默认构造函数 程序员一旦定义了构造函数,不管是否有参数。编译程序不再提供默认构造函数。 class TDate { public: TDate(int, int, int );//带参数 int IsLeapYear(); void Print(); private: int year; int month; int day; }; ... TDate d; //编译会出错,没有可用的构造//函数,对象不能被创建

析构函数 面向对象的程序设计思想认为在对象所占存储空间被回收前,应对对象作必要的善后清理,且善后工作应由编译程序负责处理。 C++中可以为每个类定义析构函数,负责该类对象所占存储空间回收前的清理工作。 析构函数是类的一个特殊成员函数。函数名由~加类名构成,没有返回值,没有参数,不能重载,可以在类的内部进行定义,也可以在外部进行定义。 析构函数在对象生命结束时自动被调用(即所占存储空间被回收前)。 程序员不能直接显式调用析构函数。

析构函数 class TDate { public: TDate(int, int, int ); int IsLeapYear(); void Print(); ~TDate();//析构函数 private: int year; int month; int day; }; TDate::~TDate() { cout<<“bye!”<<endl; } int TDate::~TDate() { cout<<“bye!”<<endl; } TDate::~TDate(int i) { cout<<“bye!”<<endl; }

析构函数 TDate gd; int main() { TDate ld; ... TDate *pd = new TDate; delete pd;//为动态分配的对象(pd指向的对象)自动调用析构函数~TDate() //为局部对象ld自动调用析构函数~TDate() } //为全局对象gd自动调用析构函数~TDate()

析构函数 析构函数有时候显得很重要,例如在类中有指针成员,且该指针指向了一片动态分配的存储空间,此时应定义析构函数,并且在析构函数中释放动态分配的存储空间,否则就会引起内存泄漏问题。 XYZ::XYZ() { cptr = new char[100]; } XYZ::~XYZ() { delete[] cptr ; class XYZ { public: XYZ(); ~XYZ(); private: char* cptr; }; int func() { XYZ a; ... }

new和delete 为什么C++中不使用malloc(…)和free(…)而使用new和delete来创建动态对象和回收动态对象? 用new创建动态对象自动调用构造函数。而malloc(…)不自动调用构造函数。 用delete回收动态对象自动调用析构函数。而free(…)不自动调用析构函数。

对象数组的构造和析构 可以定义对象的数组,此时要为每一个数组元素调用构造函数。 TDate d[100]; //调用100 次构造函数 对象数组生命期终结时,也要为每一个数组元素调用析构函数。 void func() { TDate d[100]; //调用100 次构造函数 … //调用100 次析构函数 }

对象成员的构造和析构 C++中,每个类的构造函数只负责初始化该类的对象,若类中某个成员也是对象,则该成员由成员自身的构造函数完成初始化。 若类中某个成员是一个对象,则调用该类的构造造函数前会自动调用对象成员的构造函数。 class A { public: A() { a = 0; cout <<"in A."<< endl; } private: B x;//成员x是一个B类对象 int a; }; class B { public: B() { b = 0; cout<<"in B."<< endl; } private: int b; }; int main() { A obj1; // obj1.x.b == ? obj1.a == ? // 程序运行后,控制台窗口会如何显示 } //显示如下: in B. in A.

对象成员的构造和析构 若类中某个成员是一个对象,则调用该类的析构函数后会自动调用对象成员的析构函数。(和构造函数相比,调用顺序相反) class A { public: A() { a = 0; cout<<"in A."<<endl; } ~A() { cout<<"out A."<<endl; private: B x;//成员x是对象 int a; }; class B { public: B() { b = 0; cout <<"in B."<< endl; } ~B() { cout <<"out B."<< endl; private: int b; }; int main() { A obj1; // obj1.x.b == ? obj1.a == ? // 程序运行后,控制台窗口会如何显示 } //显示如下: in B. in A. out A. out B.

对象成员的构造和析构 如何调用对象成员的有参数的构造函数? class A { public: A() { a = 0; cout<<“a="<<a<<endl; } A( int ai) { a = ai; cout <<“a="<<a<<endl; A( int ai, int bi):x(bi) { private: B x;//成员x是一个B类对象 int a; }; 如何调用对象成员的有参数的构造函数? int main() { A obj1; A obj2(10); A obj3(10, 20); // 程序运行后,控制台窗口会如何显示 } //显示如下: b=0 a=0 a=10 b=20 class B { public: B() { b = 0; cout<<"b="<<b<<endl; } B( int bi ) { b = bi; private: int b; };

拷贝构造函数 在C语言中,可以用已定义的变量初始化同类型变量。如: int a = 5; int b = a; 在C++中,也可用已定义的对象初始化同类对象。如: TDate d1(2,10,1997); TDate d2(d1); 也可以写做: TDate d1(2,10,1997); TDate d2 = d1; 用一个对象初始化另一个对象,通常意味着数据成员的逐个复制。 用一个对象初始化另一个对象需要为类定义拷贝构造函数。在初始化时会自动调用拷贝构造函数,程序员不能直接显式调用拷贝构造函数。

拷贝构造函数 拷贝构造函数是类的构造函数,带有一个参数,参数是是该类对象的引用。 class T { int a; public: T(T& t) { //拷贝构造函数 a = t.a } ... };

拷贝构造函数 class TDate { public: TDate(); TDate(int, int, int ); int IsLeapYear(); void Print(); ~TDate(); private: int year; int month; int day; }; ... TDate::TDate( TDate& d ) { year = d.year; month = d.month; day = d.day; cout<<“copying...”<<endl; }

拷贝构造函数 在下列情况下,会自动调用拷贝构造函数: a)用一个已经存在的对象初始化一个新创建的同类对象。 TDate func() { TDate td; ... return td; } int main() { TDate ld=func(); ... } //输出如下: copying... 在下列情况下,会自动调用拷贝构造函数: a)用一个已经存在的对象初始化一个新创建的同类对象。 b)对象作为函数参数时,如果采用传值调用,将建立对象的副本,该副本的构建需要调用拷贝构造函数。 int func( TDate d) { … } int main() { TDate ld; func(ld); ... } //输出为 copying... c)某个函数的返回值是一个对象,也要创建对象副本,此时也需要自动调用拷贝构造函数。

默认拷贝构造函数 程序员如果没有为类定义拷贝构造函数,编译程序会自动定义默认拷贝构造函数。 默认拷贝构造函数的语义是(数据)成员逐个依次拷贝。 如果程序员没有为类TDate定义拷贝构造函数,则编译程序定义的默认拷贝构造函数如下: TDate::TDate( TDate& d ) { year = d.year; month = d.month; day = d.day; }

默认拷贝构造函数 有时候默认拷贝构造函数会引发错误。例如在类中有指针成员,且该指针指向了一片动态分配的存储空间,此时应定义拷贝构造函数,采用默认的拷贝构造函数会引起位于两个对象中指针成员指向相同的存储空间。 Person::Person( char* pn ) { pName = new char[ strlen(pn)+1 ]; if ( pName != NULL ) strcpy(pName, pn); } Person::~Person() { delete[] pName; class Person { public: Person( char* ); ~Person(); protected: char* pName; }; //正确拷贝构造函数 Person::Person( Person& p ) { pName = new char[strlen(p.pName)+1]; if ( pName != 0 ) strcpy( pName, p.pName); } //默认拷贝构造函数 Person::Person( Person& p ) { pName = p.pName; }

默认拷贝构造函数 void main() { Person p1("王二"); Person p2(p1); ... }

上机练习内容 《C++程序设计教程》p.288 练习12-1 、12.2、12.3、12.4