C++语言程序设计教程 第7章 类与对象 第7章 类与对象.

Slides:



Advertisements
Similar presentations
C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
Advertisements

移动应用软件开发技术 第二讲:C++编程基础
第九讲 类与对象 (I)面向对象基础.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
第八章 类和对象.
C++程序设计 王希 图书馆三楼办公室.
C++语言程序设计教程 第7章 类与对象 第7章 类与对象.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
資料大樓 --談指標與陣列 綠園.
C++语言程序设计 第四章 类与对象 成都信息工程学院计算机系.
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
教材 《C++程序设计》.谭浩强. 清华大学出版社 王雪晶
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
STRUCTURE 授課:ANT 日期:2010/5/12.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
類別樣板 Class Template 類似函式樣板 由類別樣板產生的類別稱為類別樣版的實體(instance)
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
第八章 类与对象 本章要求: 掌握类的含义与定义格式。 掌握类的无参构造函数、带参构造函数、拷贝构造函数和赋值重载函数的定义格式及作用。
C++程序设计 string(字符串类) vector(容器类).
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
Ch02-基礎語法.
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
第二章 基本数据类型及运算 C数据类型概述 基本数据类型 运算符和表达式 混合运算与类型转换 数据的输入输出 顺序程序设计举例.
C#程序设计基础 $3 成员、变量和常量.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第三章 控制语句 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
物件導向程式設計 CH2.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++程式設計入門 變數與運算子 作者:黃建庭.
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
面向对象技术 练习 ffh.
第二章 类型、对象、运算符和表达式.
Review 1~3.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
第 9 章 建構函式與解構函式.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
Class 2005/05/25.
JAVA 程式設計與資料結構 第三章 物件的設計.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
變數與資料型態  綠園.
資料!你家住哪裏? --談指標 綠園.
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
第十二章 C与C C转入C++时不需改变的内容 12.2 C转入C++的一些与类无关的 新特性
Presentation transcript:

C++语言程序设计教程 第7章 类与对象 第7章 类与对象

第7章 类与对象 学习目标 1. 掌握类的定义,会根据需求设计类; 2. 会根据类创建各种对象; 3. 掌握对象的各种成员的使用方法; C++语言程序设计教程 第7章 类与对象 第7章 类与对象 学习目标 1. 掌握类的定义,会根据需求设计类; 2. 会根据类创建各种对象; 3. 掌握对象的各种成员的使用方法; 4. 会设计构造函数与拷贝构造函数来初始化对象;理解其调用过程与顺序; 5. 理解浅拷贝与深拷贝的概念; 6. 掌握动态对象以及动态对象数组的建立与释放。 7. 理解类的静态成员的概念; 8. 理解友元函数与友元类的概念; 9. 掌握常对象与常成员的使用; 10.了解对象在内存中的分布情况。

7.1 类与对象 在面向对象的程序设计中,程序模块是由类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。 C++语言程序设计教程 第7章 类与对象 7.1 类与对象 在面向过程的结构化程序设计中,程序模块是由函数构成,函数将对数据处理的语句放在函数体内,完成特定的功能,数据则通过函数参数传递进入函数体。 在面向对象的程序设计中,程序模块是由类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。 面向对象的程序设计方法就是运用面向对象的观点来对现实世界中的各种问题进行抽象,然后用计算机程序来描述并解决该问题,这种描述和处理是通过类与对象实现的。 类与对象是C++程序设计中最重要的概念

C++语言程序设计教程 第7章 类与对象 7.1 类与对象 【例7-1】 模拟时钟 分析:不管什么样的时钟, 也不管各种时钟是如何运行的, 它都具有时、分、秒3个属性。除了运行、显示时间的基本功能外,还有设置(调整)时间、设置闹钟等功能。将时钟的这些属性与功能抽象出来,分别给出面向过程的程序与面向对象的程序来实现对时钟的模拟。 思考:二者有何不同?

7.1.2 类的定义 说明: 简单讲,类是一个包含函数的结构体。因此,类的定义与结构类型的定义相似,其格式如下: class 类名 { 第7章 类与对象 7.1.2 类的定义 简单讲,类是一个包含函数的结构体。因此,类的定义与结构类型的定义相似,其格式如下: class 类名 { public: 公有数据成员或公有函数成员的定义; protected: 保护数据成员或保护函数成员的定义; private: 私有数据成员或私有函数成员的定义; }; 说明: 关键字class表明定义的是一个类; 类名是类的名称,应是一个合法的标识符; public、protected、private为存取控制属性(访问权限),用来控制对类的 成员的存取。如果前面没有标明访问权限,默认访问权限为private。 类的成员有数据成员与函数成员两类,类的数据成员和函数成员统称为类的 成员,类的数据成员一般用来描述该类对象的属性,称为属性;函数成员是描 述类行为,称作方法。函数成员由函数构成,这些作为类成员的函数因此也 叫成员函数。

程序解释: 运行结果: 0:0:0 -85893460:-85893460:-5893460 8:30:30 8:30:30 C++语言程序设计教程 第7章 类与对象 ***************************** * p7_1_b.cpp * * 面向对象的时钟程序 * ******************************/ #include <iostream> using namespace std; class Clock { private: int H,M,S; public: void SetTime(int h,int m,int s) { H=(h>=0&&h<24)?h:0; M=(m>=0&&m<60)?m:0; S=(s>=0&&s<60)?s:0; } void ShowTime() cout<<H<<":"<<M<<":"<<S<<endl; }; void main(void) { Clock MyClock; MyClock.ShowTime(); MyClock.SetTime(8,30,30); /***************************** * p7_1_a.cpp * * 面向过程的时钟程序 * struct Clock { Clock MyClock; void SetTime(int H,int M,int S) MyClock.H=(H>=0&&H<24)?H:0; MyClock.M=(M>=0&&M<60)?M:0; MyClock.S=(S>=0&&S<60)?S:0; cout<<MyClock.H<<":"; cout<<MyClock.M<<":"; cout<<MyClock.S<<endl; { ShowTime(); SetTime(8,30,30); ShowTime(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 时钟程序B 时钟程序A 程序解释: 通过对上述两种方案的程序进行简单的观察,可以发现它们存在下面几点不同: 在程序A中,时钟数据用一个结构型的变量存储,对时钟数据的存取通过函数实现。由于存 储时钟数据的是一个全局变量,在任何地方都可见,可以不通过函数单独存取时钟数据。 程序B中,只能通过类提供的函数操作时钟。 程序A中,数据与对数据操作相互独立,数据作为参数传递给函数。程序B中,数据与对数 据的操作构成一个整体。 程序A与程序B运行的初始结果不同。这是因为在程序A中,变量是全局的;在程序B中, 对 象(变量)MyClock是函数main()中的局部对象。全局变量与局部变量在没有初始化时,取初 值方式不同,这样造成了运行结果不同。将第23行移出main()外,使之变成全局对象后, 两程序结果完全相同。 在程序B中发现,一个以class开头的类似结构体的结构,将时钟的数据与对数据进行处理 的函数包括在一起,这就是用C++实现的类。 运行结果: 0:0:0 -85893460:-85893460:-5893460 8:30:30 8:30:30

C++语言程序设计教程 第7章 类与对象 7.1.2 类的定义 例如:例7-1中定义了一个时钟类Clock。

7.1.2 类的定义 类定义中的数据成员描述了类对象所包含的数据类型,数据成员的类型可以是C++基本数据类型,也可以是构造数据类型。 第7章 类与对象 7.1.2 类的定义 1. 数据成员 类定义中的数据成员描述了类对象所包含的数据类型,数据成员的类型可以是C++基本数据类型,也可以是构造数据类型。 struct Record { char name[20]; int score; }; class Team { private: int num; //基本数据类型 Record *p; //构造数据类型

7.1.2 类的定义 注意: class Team; //已定义的类 class Grade { Team a; // 使用了已定义的类类型 第7章 类与对象 7.1.2 类的定义 class Team; //已定义的类 class Grade { Team a; // 使用了已定义的类类型 Grade *p; // 使用正在定义的类类型定义指针成员 Grade &r; // 使用正在定义的类类型定义引用成员 Grade b; // 错误! 使用了未定义完的类Record 定义变量 }; 注意: 因为类只是一种类型,类中的数据成员不占内存空间,因此在定义数据成员 时不能给数据成员赋初值。 类的数据成员除了可以使用前面讲述的C++类型外,还可以使用已定义完整的 类类型。 在正在定义的类中,由于该类型没有定义完整,所以不能定义该类类型的变 量,只能定义该类类型的指针成员以及该类类型的引用成员。

C++语言程序设计教程 第7章 类与对象 7.1.2 类的定义 2. 成员函数 作为类成员的成员函数描述了对类中的数据成员实施的操作。成员函数的定义、声明格式与非成员函数(全局函数)的格式相同。成员函数可以放在类中定义,也可以放在类外。放在类中定义的成员函数为内联(inline)函数。 Clock类中的成员函数就是放在类内定义的。 C++可以在类内声明成员函数的原型,在类外定义函数体。这样做的好处是相当于在类内列了一个函数功能表,使我们对类的成员函数的功能一目了然,避免了在各个函数实现的大堆代码中查找函数的定义。在类中声明函数原型的方法与一般函数原型的声明一样,在类外定义函数体的格式如下: ::是类的作用域分辨符,用在此处,放在类名后成员函数前,表明后面的成员函数属于前面的那个类。 返回值类型 类名 :: 成员函数名(形参表) { 函数体; }

7.1.2 类的定义 C++语言程序设计教程 Clock类中的成员函数可以在类中声明: class Clock { private: 第7章 类与对象 7.1.2 类的定义 Clock类中的成员函数可以在类中声明: class Clock { private: int H,M,S; public: void SetTime(int h,int m,int s); //声明成员函数 void ShowTime(); //声明成员函数 }; 在类外实现成员函数如下: void Clock::SetTime(int h,int m,int s) { H=h,M=m,S=s; }; void Clock:: ShowTime() cout<<H<<":"<<M<<":"<<S<<endl; }

C++语言程序设计教程 第7章 类与对象 7.1.2 类的定义 如果要将类外定义的成员函数编译成内联函数,可以在类外定义函数时,函数的返回类型前加上inline; 下面将ShowTime()定义成内联函数,与在类内定义成员函数的效果相同。 inline void Clock::ShowTime() { cout<<H<<":"<<M<<":"<<S<<endl; } 3. 类作用域 类是一组数据成员和函数成员的集合,类作用域作用于类中定义的特定的成员,包括数据成员与成员函数,类内的每一个成员都具有类作用域。实际上,类的封装作用也就是限制类的成员其访问范围局限于类的作用域之内。

7.1.2 类的定义 注意: class Clock { private: int H,M,S; public: 第7章 类与对象 7.1.2 类的定义 class Clock { private: int H,M,S; public: Clock AddTime(Clock C2){ // 形参为Clock类型的变量 Clock T; // 函数体中定义了Clock类型的变量 ... return T; // 返回类型为Clock类型 } }; 注意: 在成员函数中不仅可以自由使用类的成员,还可以使用该类定义变量(对象),通过变量使用成员。其原因是函数在调用时才在栈内存中建立函数体中的变量(包括实参),这时类已经定义完毕,当然可以使用已定义完整的类类型的变量。

C++语言程序设计教程 第7章 类与对象 7.1.3 对象的建立与使用 类相当于一种包含函数的自定义数据类型,它不占内存,是一个抽象的“虚”体,使用已定义的类建立对象就像用数据类型定义变量一样。对象建立后,对象占据内存,变成了一个“实”体。类与对象的关系就像数据类型与变量的关系一样。其实,一个变量就是一个简单的不含成员函数的数据对象。 类名 对象名; 建立对象的格式如下: 其中,对象名可以是简单的标识符,也可以是数组。 在例7-1中,使用: Clock MyClock; 建立了一个Clock型的对象MyClock。 类名 对象名;

7.1.3 对象的建立与使用 注意: 在建立对象后,就可以通过对象存取对象中的数据成员,调用成员函数。存取语法如下: 对象名.属性 C++语言程序设计教程 第7章 类与对象 7.1.3 对象的建立与使用 在建立对象后,就可以通过对象存取对象中的数据成员,调用成员函数。存取语法如下: 例如,通过对象MyClock使用成员函数SetTime()的方式如下: MyClock.SetTime(8,30,30); 至于对数据成员H、M、S的存取,因其存取权限为pravate而被保护, 所以不能进行直接存取。 对象名.属性 对象名.成员函数名(实参1, 实参2,…,) 注意: 为节省内存,编译器在创建对象时,只为各对象分配用于保存各对象数据成员初始化的值,并不为各对象的成员函数分配单独的内存空间,而是共享类的成员函数定义,即类中成员函数的定义为该类的所有对象所共享,这是C++编译器创建对象的一种方法,在实际应用中,我们仍要将对象理解为由数据成员和函数成员两部分组成。

7.1.4 成员的存取控制 public 公开(公有)级 该类成员以及所有对象 protected 保护级 该类及其子类成员 private 第7章 类与对象 7.1.4 成员的存取控制 通过设置成员的存取控制属性, 使对类成员的存取得到控制,从而达到了信息隐藏的目的。C++的存取控制属性有:公有类型(public)、私有类型(private)和保护类型(protected)。三者的意义如下: 表7-1 存取控制属性表 存取属性 意 义 可存取对象 public 公开(公有)级 该类成员以及所有对象 protected 保护级 该类及其子类成员 private 私有级 该类的成员 类中定义为public等级的成员,可以被该类的任何对象存取,适用于完全公开的数据。而private等级的成员只可被类内的成员存取,适用于不公开的数据。至于protected等级,属于半公开性质的数据,定义为protected等级的成员,可以被该类及其子类存取。关于子类的概念,在以后的章节中讲述。

C++语言程序设计教程 第7章 类与对象 7.1.4 成员的存取控制 在Clock类中,H、M、S的存取控制属性为privated。这样,这些数据不能在类外存取而被保护,下列存取方法是错误的: MyClock.M=30; 而成员函数SetTime()、ShowTime()存取控制属性为public, 因此在类外可以通过对象存取。 由于private成员被隐藏起来,不能直接在类外被存取,为了取得这些被隐藏的数据,通常在类内定义一个public的成员函数,通过该成员函数存取private成员,而public的成员函数又能在类外被调用。这样通过调用public型的成员函数,间接存取到private成员。这样的函数起到了为private成员提供供外界访问的接口作用。 类Clock中成员函数SetTime()、ShowTime()就是存取private数据成员H、M、S的接口。 通过接口访问类的数据成员,一方面有效保护数据成员,另一方面又保证了数据的合理性。

C++语言程序设计教程 第7章 类与对象 7.2 构造函数与析构函数 在定义一个对象的同时,希望能给它的数据成员赋初值――对象的初始化。在特定对象使用结束时,还经常需要进行一些清理工作。C++程序中的初始化和清理工作分别由两个特殊的成员函数来完成,它们就是构造函数和析构函数。

C++语言程序设计教程 第7章 类与对象 7.2.1 构造函数 构造函数(constructor) 构造函数是与类名相同的,在建立对象时自动调用的函数。如果在定义类时,没有为类定义构造函数,编译系统就生成一个默认形式的隐含的的构造函数,这个构造函数的函数体是空的,因此默认构造函数不具备任何功能。 如果用户至少为类定义了一个构造函数,C++就不会生成任何默认的构造函数,而是根据对象的参数类型和个数从用户定义的构造函数中选择最合适的构造函数完成对该对象的初始化。 作为类的成员函数,构造函数可以直接访问类的所有数据成员,可以是内联函数,可以不带任何参数,可以带有参数表以及默认形参值,还可以重载,用户可以根据不同问题的具体需要,有针对性地设计合适的构造函数将对象初始化为特定的状态。

7.2.1 构造函数 例如,将例7-1 程序p7-1_b.cpp中的Clock类中添加带有默认形参值的构造函数: 第7章 类与对象 7.2.1 构造函数 例如,将例7-1 程序p7-1_b.cpp中的Clock类中添加带有默认形参值的构造函数: Clock(int h=0,int m=0,int s=0) { H=(h>=0&&h<24)?h:0; M=(m>=0&&m<60)?m:0; S=(s>=0&&s<60)?s:0; } 执行:Clock MyClock; MyClock.ShowTime(); 显示结果为: 0:0:0 这是因为建立对象时调用了Clock(),各个形参被设成了默认值。 当执行: Clock MyClock(9,30,45); MyClock.ShowTime(); 9:30:45 这是因为建立对象时调用了Clock(9,30,45)

7.2.1 构造函数 构造函数的函数名必须与定义它的类同名。 构造函数没有返回值。如果在构造函数前加void是错误的。 C++语言程序设计教程 第7章 类与对象 7.2.1 构造函数 构造函数是类的一个成员函数,除了具有一般成员函数的特征之外,还归纳出如下特殊的性质: 构造函数的函数名必须与定义它的类同名。 构造函数没有返回值。如果在构造函数前加void是错误的。 构造函数被声明定义为公有函数。 构造函数在建立对象时由系统自动调用。 注意:由于构造函数可以重载,可以定义多个构造函数,在建立对象时根据参数来调用相应的构造函数。如果相应的构造函数没有定义,则出错。例如,若定义例7-1中的构造函数,而不是带默认形参值的构造函数: Clock(int h, int m,int s) { H=(h>=0&&h<24)?h:0; M=(m>=0&&m<60)?m:0; S=(s>=0&&s<60)?s:0; } 定义对象Clock MyClock;时,调用Clock(), 而Clock类没有Clock()函数,因而出错。

C++语言程序设计教程 第7章 类与对象 7.2.2 析构函数 自然界万物都是有生有灭,程序中的对象也是一样。对象在定义时诞生,不同生存期的对象在不同的时期消失。在对象要消失时,通常有一些善后工作需要做, 例如:构造对象时,通过构造函数动态申请了一些内存单元,在对象消失之前就要释放这些内存单元。C++用什么来保证这些善后清除工作的执行呢?答案是:析构函数。 析构函数(destructor)也译作拆构函数, 是在对象消失之前的瞬间自动调用的函数, 其形式是: 构造函数名(类名)前加上一个逻辑非运算符~,以示与构造函数相反。 析构函数与构造函数的作用几乎正好相反,相当于“逆构造函数”。析构函数也是类的一个特殊的公有函数成员, ~构造函数名();

7.2.2 析构函数 析构函数具有以下特点: 析构函数没有任何参数,不能被重载,但可以是虚函数,一个类只有一个析构函数。 C++语言程序设计教程 第7章 类与对象 7.2.2 析构函数 析构函数具有以下特点: 析构函数没有任何参数,不能被重载,但可以是虚函数,一个类只有一个析构函数。 析构函数没有返回值。 析构函数名与类名相同,但在类名前加上一个逻辑非运算符“~”,以示与构造函数对比区别。 析构函数一般由用户自己定义,在对象消失时由系统自动调用,如果用户没有定义析构函数,系统将自动生成一个不做任何事的默认析构函数。 注意: 在对象消失时的清理工作并不是由析构函数完成,而是靠用户在析构函数中添加清理语句完成。

{ i=4 i=5 i=3 i=2   } } C++语言程序设计教程 第7章 类与对象 7.2.2 析构函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /***************************** * p7_2.cpp * * 构造函数与析构函数 * *****************************/ #include <iostream> using namespace std; class Clock { private: int H,M,S; public: Clock(int h=0,int m=0,int s=0) { H=h,M=m,S=s; cout<<"constructor:"<<H<<":"<<M<<":"<<S<<endl; }

C++语言程序设计教程 第7章 类与对象 7.2.2 析构函数 16 17 18 19 20 21 22 23 24 25 26 27 ~Clock() { cout<<"destructor:"<<H<<":"<<M<<":"<<S<<endl; } }; Clock C1(8,0,0); Clock C2(9,0,0); void main(void) Clock C3(10,0,0); Clock C4(11,0,0); 运行结果: constructor:8:0:0 constructor:9:0:0 constructor:10:0:0 constructor:11:0:0 destructor:11:0:0 destructor:10:0:0

/********************************** * p7_3.cpp * * 基本的字符串类 * 第7章 类与对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /********************************** * p7_3.cpp * * 基本的字符串类 * **********************************/ #include <iostream> using namespace std; class String { private: char *Str; int len; public: void ShowStr() { cout<<"string:"<<Str<<",length:"<<len<<endl; } String() len=0; Str=NULL;

运行结果: string:ABCDE, length:5 string:123456, length:6 C++语言程序设计教程 第7章 类与对象 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 String(const char *p) { len=strlen(p); Str=new char[len+1]; strcpy(Str,p); } ~String() if (Str!=NULL) delete [] Str; Str=NULL; }; void main(void) { char s[]="ABCDE"; String s1(s); String s2("123456"); s1.ShowStr(); s2.ShowStr(); 运行结果: string:ABCDE, length:5 string:123456, length:6

7.2.3 拷贝构造函数 拷贝构造函数是与类名相同,形参是本类的对象的引用的函数,在用已存在对象初始化新建立对象时调用。 C++语言程序设计教程 第7章 类与对象 7.2.3 拷贝构造函数 拷贝构造函数是与类名相同,形参是本类的对象的引用的函数,在用已存在对象初始化新建立对象时调用。 类的拷贝构造函数一般由用户定义,如果用户没有定义拷贝构造函数,系统就会自动生成一个默认函数,这个默认拷贝构造函数的功能是把初始值对象的每个数据成员的值依次复制到新建立的对象中。因此,也可以说是完成了同类对象的克隆(Clone)。这样得到的对象和原对象具有完全相同的数据成员,即完全相同的属性。

7.2.3 拷贝构造函数 定义一个拷贝构造函数的一般形式为: 类名(类名& 对象名) C++语言程序设计教程 第7章 类与对象 7.2.3 拷贝构造函数 用户可以也可以根据实际问题的需要定义特定的拷贝构造函数来改变缺省拷贝构造函数的行为,以实现同类对象之间数据成员的传递。如果用户自定义了拷贝构造函数,则在用一个类的对象初始化该类的另外一个对象时,自动调用自定义的拷贝构造函数。 定义一个拷贝构造函数的一般形式为: 拷贝构造函数在用类的一个对象去初始化该类的另一个对象时调用,以下三种情况相当于用一个已存在的对象去初始化新建立的对象, 此时, 调用拷贝构造函数: ① 当用类的一个对象去初始化该类的另一个对象时。 ② 如果函数的形参是类的对象,调用函数时,将对象作为函数实参传递给函数的形参时。 ③ 如果函数的返回值是类的对象,函数执行完成,将返回值返回时。 类名(类名& 对象名) { … };

/***************************** * p7_4.cpp * * 构造拷贝构造函数 * 第7章 类与对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /***************************** * p7_4.cpp * * 构造拷贝构造函数 * *****************************/ #include <iostream> using namespace std; class Clock { private: int H,M,S; public: Clock(int h=0,int m=0,int s=0) { H=h,M=m,S=s; cout<<"constructor:"<<H<<":"<<M<<":"<<S<<endl; } ~Clock() cout<<"destructor:"<<H<<":"<<M<<":"<<S<<endl;

注意: 运行结果: 拷贝构造函数只是在用一个已存在的对象去初始化新建立的对象时调用,在对 象进行赋值时,拷贝构造函数不被调用。 C++语言程序设计教程 第7章 类与对象 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Clock(Clock & p) { cout<<"copy constructor,before call: "<<H<<":"<<M<<":"<<S<<endl; H=p.H; M=p.M; S=p.S; } void ShowTime() cout<<H<<":"<<M<<":"<<S<<endl; }; Clock fun(Clock C) return C; void main(void) Clock C1(8,0,0); Clock C2(9,0,0); Clock C3(C1); fun(C2); Clock C4; C4=C2; 运行结果: constructor:8:0:0 constructor:9:0:0 copy constructor,before call: -858993460:-858993460:-858993460 copy constructor,before call: 1310592:4200534:1310568 destructor:9:0:0 constructor:0:0:0 destructor:8:0:0 注意: 拷贝构造函数只是在用一个已存在的对象去初始化新建立的对象时调用,在对 象进行赋值时,拷贝构造函数不被调用。 用一个常量初始化新建立的对象时,调用构造函数,不调用拷贝构造函数。 建立对象时,构造函数与拷贝构造函数有且仅有一个被调用。

C++语言程序设计教程 第7章 类与对象 7.2.4 浅拷贝与深拷贝 在默认的拷贝构造函数中,拷贝的策略是直接将原对象的数据成员值依次拷贝给新对象中对应的数据成员,如前面示例p7_4.cpp中定义的拷贝函数所示,那么我们为何不直接使用系统默认的拷贝构造函数,何必又自己定义一个拷贝构造函数呢?但是,有些情况下使用默认的拷贝构造函数却会出现意想不到的问题。 例如,使用程序p7_3.cpp中定义的String类,执行下列程序系统就会出错: void main() { String s1("123456"); String s2=s1; }

C++语言程序设计教程 第7章 类与对象 7.2.4 浅拷贝与深拷贝 为什么会出错呢?程序中首先创建对象s1, 为对象s1分配相应的内存资源,调用构造函数初始化该对象,然后调用系统缺省的拷贝构造函数将对象s1拷贝给对象s2,这一切看来似乎很正常,但程序的运行却出现异常! 原因在于默认的拷贝构造函数实现的只能是浅拷贝,即直接将原对象的数据成员值依次拷贝给新对象中对应的数据成员,并没有为新对象另外分配内存资源。这样,如果对象的数据成员是指针,两个指针对象实际上指向的是同一块内存空间。 当执行String s2=s1时,默认的浅拷贝构造函数进行的是下列操作: s2.len=s1.len; s2.Str=s1.Str; 实际上是将s1.Str的地址赋给了s2.Str,并没有为s2.Str分配内存,执行String s2=s1;后,对象s2析构,释放内存,然后对象s1析构,由于s1.Str 和s2.Str所占用的是同一块内存,而同一块内存不可能释放两次,所以当对象s1析构时,程序出现异常,无法正常执行和结束。 由此可见,在某些情况下,浅拷贝会带来数据安全方面的隐患。

C++语言程序设计教程 第7章 类与对象 7.2.4 浅拷贝与深拷贝 当类的数据成员中有指针类型时,我们就必须定义一个特定的拷贝构造函数,该拷贝构造函数不仅可以实现原对象和新对象之间数据成员的拷贝,而且可以为新的对象分配单独的内存资源,这就是深拷贝构造函数。

7.2.4 浅拷贝与深拷贝 注意: 【例7-5】 带深拷贝构造函数的字符串类。 C++语言程序设计教程 第7章 构造数据类型 7.2.4 浅拷贝与深拷贝 【例7-5】 带深拷贝构造函数的字符串类。 在程序p7_3中的String类中,加入下列拷贝构造函数,构成了带深拷贝函数的字符串类。 String(String & r) { len=r.len; if(len!=0) Str=new char[len+1]; strcpy(Str,r.Str); } 下列程序能正常运行。 void main(void){ String s1("123456"); String s2=s1; s1.ShowStr(); s2.ShowStr(); 注意: 在重新定义拷贝构造函数后,默认拷贝构造函数与默认构造函数就不存在了, 如果在此时调用默认构造函数就会出错。 在重新定义构造函数后,默认构造函数就不存在了,但默认拷贝构造函数还存 在。 在对象进行赋值时,拷贝构造函数不被调用。此时进行的是结构式的拷贝。

C++语言程序设计教程 第7章 类与对象 7.3 对象的使用 类相当于一种包含函数的自定义数据类型,它不占内存,是一个抽象的“虚”体,使用已定义的类建立对象就像用数据类型定义变量一样。对象建立后,对象占据内存,变成了一个“实”体。 对象如同一般变量,占用一块连续的内存区域,因此可以使用一个指向对象的指针来访问对象,即对象指针,它指向存放该对象的地址。可用类来定义对象指针变量,通过对象指针来访问对象的成员。

7.3.1 对象指针 对象指针遵循一般变量指针的各种规则,其语法定义形式如下: 类名 *对象指针名; C++语言程序设计教程 第7章 类与对象 7.3.1 对象指针 对象指针遵循一般变量指针的各种规则,其语法定义形式如下: 如同通过对象名访问对象的成员一样,使用对象指针也只能访问该类的公有数据成员和函数成员,但与前者使用“.”运算符不同,对象指针采用“->”运算符访问公有数对象指针名->数据成员名 例如: Clock *Cp; Clock C1(8,0,0); Clock *Cp; Cp=&C1; Cp->ShowTime(); 类名 *对象指针名; 对象指针名->数据成员名 或: 对象指针名->成员函数名(参数表)

C++语言程序设计教程 第7章 类与对象 7.3.1 对象指针 在C++中,对象指针可以作为成员函数的形参,一般而言,使用对象指针作为函数的参数要比使用对象作为函数的参数更普遍一些,因为使用对象指针作为函数的参数有如下两点好处: (1) 实现地址传递。 通过在函数调用时将实参对象的地址传递给形参指针对象,使形参指针对象和实参对象指向同一内存地址,这样,对象指针所指向对象的改变也将同样影响着实参对象,从而实现信息的双向传递。 (2) 使用对象指针效率高 使用对象指针传递的仅仅是对应实参对象的地址,并不需要实现对象之间的副本拷贝,这样就会减小时空开销,提高运行效率。

【例7-6】 时间加法。时间加法有两种, 一种是时钟加秒数, 另一种是时钟加时、分、秒。采用重载函数实现这两种加法。 1 2 3 4 5 6 C++语言程序设计教程 第7章 类与对象 【例7-6】 时间加法。时间加法有两种, 一种是时钟加秒数, 另一种是时钟加时、分、秒。采用重载函数实现这两种加法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /***************************** * p7_6.cpp * * 带时间加法的时钟类 * *****************************/ #include <iostream> using namespace std; class Clock { private: int H,M,S; public: void SetTime(int h,int m,int s) { H=h,M=m,S=s; } void ShowTime() cout<<H<<":"<<M<<":"<<S<<endl;

C++语言程序设计教程 第7章 类与对象 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 Clock(int h=0,int m=0,int s=0) { H=h,M=m,S=s; } Clock(Clock & p) H=p.H, M=p.M, S=p.S; void TimeAdd(Clock *Cp); void TimeAdd(int h,int m,int s); void TimeAdd(int s); }; void Clock::TimeAdd(Clock *Cp) H=(Cp->H+H+(Cp->M+M+(Cp->S+S)/60)/60)%24; M=(Cp->M+M+(Cp->S+S)/60)%60; S=(Cp->S+S)%60; 函数重载 函数原型声明 对象指针

运行结果: 1:6:40 9:27:0 函数重载 函数重载 C++语言程序设计教程 第7章 类与对象 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void Clock::TimeAdd(int h,int m,int s) { H=(h+H+(m+M+(s+S)/60)/60)%24; M=(m+M+(s+S)/60)%60; S=(s+S)%60; } void Clock::TimeAdd(int s) H=(H+(M+(S+s)/60)/60)%24; M=(M+(S+s)/60)%60; S=(S+s)%60; void main() Clock C1; Clock C2(8,20,20); C1.TimeAdd(4000); C1.ShowTime(); C2.TimeAdd(&C1); C2.ShowTime(); 函数重载 函数重载 运行结果: 1:6:40 9:27:0

C++语言程序设计教程 第7章 类与对象 7.3.2 对象引用 对象引用就是对某类对象定义一个引用,其实质是通过将被引用对象的地址赋给引用对象,使二者指向同一内存空间,这样引用对象就成为了被引用对象的“别名”。 对象引用的定义方法与基本数据类型变量引用的定义是一样的。定义一个对象引用,并同时指向一个对象的格式为: 类名 & 对象引用名=被引用对象; 注意: 对象引用与被引用对象必须是同类型的。 除非是作为函数参数与函数返回值,对象引用在定义时必须要初始化。 定义一个对象引用并没有定义一个对象,所以不分配任何内存空间,不 调用构造函数。

7.3.2 对象引用 对象引用的使用格式为: 例如: Clock C1(8,20,20); 第7章 类与对象 7.3.2 对象引用 对象引用的使用格式为: 例如: Clock C1(8,20,20); Clock & Cr=C1; //定义了C1的对象引用Cr。 Cr.ShowTime(); //通过对象引用使用对象的成员 运行结果为: 8:20:20 对象引用名 . 数据成员名 或: 对象引用名 . 成员函数名(参数表)

C++语言程序设计教程 第7章 类与对象 7.3.2 对象引用 对象引用的优点: 对象引用通常用作函数的参数,它不仅具有对象指针的优点,而且比对象指针更简洁,更方便,更直观。将p7_6.cpp中添加如下函数: void Clock::TimeAdd(Clock & Cr) { H=(Cr.H+H+(Cr.M+M+(Cr.S+S)/60)/60)%24; M=(Cr.M+M+(Cr.S+S)/60)%60; S=(Cr.S+S)%60; } 将C2.TimeAdd(&C1); 替换为:C2.TimeAdd(C1); 运行结果与p7_6.cpp一样。

7.3.3 对象数组 对象数组是以对象为元素的数组。对象数组的定义、赋值、引用与普通数组一样,只是数组元素与普通数组的数组元素不同。 C++语言程序设计教程 第7章 类与对象 7.3.3 对象数组 对象数组是以对象为元素的数组。对象数组的定义、赋值、引用与普通数组一样,只是数组元素与普通数组的数组元素不同。 对象数组定义格式如下: 其中,类名指出该数组元素所属的类,常量表达式给出某一维元素的个数。 与结构数组不同,对象数组初始化需要使用构造函数完成,以一个大小为n的一维数组为例,对象数组的初始化格式如下: 类名 对象数组名[常量表达式n],...,[ 常量表达式2][常量表达式1]; 数组名[n]={ 构造函数(数据成员1初值,数据成员2初值,…), 构造函数(数据成员1初值,数据成员2初值,…), … 构造函数(数据成员1初值,数据成员2初值,…)}; 注意:不带初始化表的对象数组,其初始化靠调用不带参数的构造函数完成。

对象数组名[下标表达式1][ 下标表达式2]…[下标表达式m].成员函数名(参数表) C++语言程序设计教程 第7章 类与对象 7.3.3 对象数组 以一个m维数组为例,对象数组元素的存取格式如下: 对象数组名[下标表达式1][ 下标表达式2]…[下标表达式m].数据成员名 或: 对象数组名[下标表达式1][ 下标表达式2]…[下标表达式m].成员函数名(参数表) 【例7-7】 计算一个班学生某门功课的总评成绩。 分析: 首先设计一个类Score,这个类的数据成员为一个学生的学号、姓名、平时成绩、期末考试成绩,成员函数有求总评成绩、显示成绩。然后,定义一个对象数组存储一个班学生的成绩。最后,通过逐一调用数组元素的成员函数求每个学生的总评成绩。

/***************************************** * p7_7.cpp * 第7章 类与对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /***************************************** * p7_7.cpp * * 求一个班学生某门功课的总评成绩 * ******************************************/ #include<iostream> using namespace std; const int MaxN=100; const double Rate=0.6; //平时成绩比例 class Score { private: long No; //学号 char *Name; //姓名 int Peace; //平时成绩 int Final; //期末考试成绩 int Total; //总评成绩 public: Score(long=0,char* = NULL,int=0,int=0,int=0); //构造函数 void Count(); //计算总评成绩 void ShowScore(); //显示成绩 };

Score::Score(long no,char *name,int peace,int final,int total) //构造函数 第7章 类与对象 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 Score::Score(long no,char *name,int peace,int final,int total) //构造函数 { No=no; Name=name; Peace=peace; Final=final; Total=total; } void Score::Count() Total=Peace*Rate+Final*(1-Rate)+0.5; void Score::ShowScore() cout<<No<<"\t"<<Name<<"\t"<<Peace<<"\t"<<Final<<"\t"<<Total<<endl;

C++语言程序设计教程 第7章 类与对象 38 39 40 41 42 43 44 45 46 47 48 49 50 void main() { Score ClassScore1[3]; Score ClassScore2[3]={Score(200607001,"Tom",80,79), Score(200607002,"John",90,85), Score(200607003,"Wilson",70,55)}; for(int i=0;i<3;i++) ClassScore2[i].Count(); for(i=0;i<3;i++) ClassScore2[i].ShowScore(); } 对象数组 运行结果: 200607001 Tom 80 79 80 200607002 John 90 85 88 200607003 Wilson 70 55 64

7.3.4 动态对象 动态对象: 动态对象是指编程者随时动态建立并可随时消失的对象。 C++语言程序设计教程 第7章 类与对象 7.3.4 动态对象 动态对象: 动态对象是指编程者随时动态建立并可随时消失的对象。 建立动态对象采用动态申请内存的语句new,删除动态对象使用delete语句。 建立一个动态对象的格式为: 对象指针=new 类名(初值表); 注意: 对象指针的类型应与类名一致。 动态对象存储在new语句从堆申请的空间中。 建立动态对象时要调用构造函数,当初值表缺省时调用默认的构造函数。

7.3.4 动态对象 例如: Clock *Cp; //建立对象指针 第7章 类与对象 7.3.4 动态对象 例如: Clock *Cp; //建立对象指针 Cp=new Clock; //建立动态对象,调用默认构造函数Clock()。 Cp->ShowTime(); //结果为0:0:0 Cp=new Clock(8,0,0); //建立动态对象,调用构造函数Clock(int,int,int) Cp->ShowTime(); //结果为8:0:0 注意: 函数体内的局部对象在函数调用时建立,在函数调用完后消失;全局对象则在程序执行时建立,执行完成后才消失;这些对象在何时建立,何时消失是C++规定好了的,不是编程者能控制的。

7.3.4 动态对象 在堆中建立的动态对象不能自动消失,需要使用delete语句删除对象,格式为: delete 对象指针; C++语言程序设计教程 第7章 类与对象 7.3.4 动态对象 在堆中建立的动态对象不能自动消失,需要使用delete语句删除对象,格式为: 在删除动态对象时,释放堆中的内存空间,在对象消失时,调用析构函数。 例如: delete Cp; //删除Cp指向的动态对象 动态对象的一个重要的使用方面是用动态对象组成动态对象数组,建立一个一维动态对象数组的格式为: 删除一个动态对象数组的格式为: 在建立动态对象数组时,要调用构造函数,调用的次数与数组的大小相同;删除对象数组时,要调用析构函数,调用次数与数组的大小相同。 delete 对象指针; 对象指针 = new 类名[数组大小]; delete [] 对象指针;

7.3.4 动态对象 将p7_7.cpp改为用动态对象数组实现如下: { No=no; Name=name; Peace=peace; 第7章 类与对象 7.3.4 动态对象 将p7_7.cpp改为用动态对象数组实现如下: Score::SetScore(long no,char *name,int peace,int final,int total) { No=no; Name=name; Peace=peace; Final=final; Total=total; }

7.3.4 动态对象 SetScore()函数为动态数组设置初值。 void main() { 第7章 类与对象 7.3.4 动态对象 将p7_7.cpp改为用动态对象数组实现如下: SetScore()函数为动态数组设置初值。 void main() { Score * ClassScore; ClassScore=new Score [3]; ClassScore[0].SetScore(200607001,"Tom",80,79), ClassScore[1].SetScore(200607002,"John",90,85), ClassScore[2].SetScore(200607003,"Wilson",70,55); for(int i=0;i<3;i++) ClassScore[i].Count(); for(i=0;i<3;i++) ClassScore[i].ShowScore(); delete [] ClassScore; }

7.3.5 This指针 例如: 1 void Clock::SetTime (int h, int m, int s) 2 { 3 第7章 类与对象 7.3.5 This指针 一个类的成员函数中,有时希望引用调用的它对象,对此,C++采用隐含的this指针来实现。 this指针是一个系统预定义的特殊指针,指向当前对象,表示当前对象的地址。 为了与类的数据成员H、M、S区别,将SetTime的形参名设为h、m、s。如果使用this指针,就可以凭this指针区分本对象的数据成员与其他变量。 1 2 3 4 5 6 例如: void Clock::SetTime (int h, int m, int s) { H=h, M=m, S=s; this->H=h, this->M=m, this->S=s; (* this).H=h, (* this).M=m, (* this).S=s; } // 语句3、4、5是等效的

C++语言程序设计教程 第7章 类与对象 7.3.5 This指针 系统利用this指针明确指出成员函数当前操作的数据成员所属的对象。实际上,当一个对象调用其成员函数时,编译器先将该对象的地址赋给this指针,然后调用成员函数,这样成员函数对对象的数据成员进行操作时,就隐含使用了this指针。 一般而言,通常不直接使用this指针来引用对象成员,但在某些少数情况下,可以使用this指针,如:重载某些运算符以实现对象的连续赋值等。 1 2 3 4 //使用this指针重新设计的SetTime()成员函数如下: void Clock::SetTime (int H, int M, int S) { this->H=H, this->M=M, this->S=S; } 注意: this指针不是调用对象的名称,而是指向调用对象的指针的名称。 this的值不能改变,它总是指向当前调用对象。

C++语言程序设计教程 第7章 类与对象

C++语言程序设计教程 第7章 类与对象

本章小结 ◇在面向对象的程序设计中,程序模块是由类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。 C++语言程序设计教程 第5章 构造数据类型 本章小结 ◇在面向对象的程序设计中,程序模块是由类构成的。类是对逻辑上相关的函数与数据的封装,它是对问题的抽象描述。 ◇ 类中有数据成员与成员函数,成员的访问控制属性有private、protected、public。类内可以访问所有控制属性的成员,在类外通过对象只能访问控制属性为public的成员。 ◇ 类是“型”,是“虚”的,不占内存,使用类建立对象后,对象是“实”的,占有内存空间。 ◇ 建立对象时调用构造函数初始化对象的数据成员,一个类提供默认的构造函数与默认的拷贝构造函数。默认的构造函数是空的,默认的拷贝构造函数的内容为浅拷贝语句。对象消失时会调用析构函数。构造函数与析构函数都可以重新定义。 ◇ 在默认的拷贝构造函数中,拷贝的策略是直接将原对象的数据成员值依次拷贝给新对象中对应的数据成员,这种方式为浅拷贝。深拷贝能将原对象指针指向的内容拷贝给新对象中。 ◇ 建立对象指针、对象引用均没有建立对象,所以此时不调用构造函数。通过对象指针使用对象的成员要用操作符->, 通过对象引用使用对象的成员要用操作符. 。 ◇ 对象数组是以对象为元素的数组,对象数组的定义、赋值、引用与普通数组一样,建立一个对象数组,相当于建立了多个对象,因此,多次调用构造函数。对象数组初始化需要使用构造函数完成。。

C++语言程序设计教程 第5章 构造数据类型 本章小结 ◇建立动态对象使用语句new,动态对象一定要用语句delete删除。建立动态对象数组使用语句new[],删除动态对象数组使用语句delete[]。 ◇ this指针是一个系统预定义的指向当前对象的指针,通过this指针可以访问对象的成员。 ◇ 组合类是含有类对象的类,组合类对象称为组合对象。定义组合对象时调用构造函数的顺序为类中成员对象定义的顺序,子对象构造时初始值通过组合类构造函数的成员对象初始化表提供。 ◇ 静态数据成员是类的数据成员,独立于类存在。在类内定义,在类外初始化。静态成员函数属于整个类,是该类的所有对象共享的成员函数。可通过类名、对象调用静态成员函数访问静态函数成员。 ◇ 友元函数不是类的成员,但它可以访问类的任何成员。一个类的友元类可以访问该类的任何成员。 ◇ 关键字const来定义的对象称为常对象,常对象的成员不允许被修改。使用const定义的数据成员称为常数据成员,常数据成员不能被更新。在定义时使用const关键字修饰的成员函数称为常成员函数,用于访问类的常对象。 ◇ 各类对象在内存中的分布与以及生命周期与普通变量一样变量一样,一个类的所有对象共有该类的成员函数,独享各自的数据成