1、第4章 面向对象的编程基础,4.1 类 4.2 结构 4.3 方法 4.4 属性与索引器,C#是一种强大的、面向对象的程序开发语言,本章将带领读者进入C#面向对象的世界,学习一些基本概念,包括对象、类等,以及如何创建和使用类的对象。 为什么要学习对象和类? 举个简单的例子:汽车厂造车需要设计图和样板,然后造出汽车。编程也是一样的,类好比是制造汽车的设计图和样板,对象好比是汽车,这都是汽车厂不可或缺的东西。,第4章 面向对象的编程基础,在现实世界中,对象就是拥有了这些特性和动作才会在这个世界上变得与众不同。因此,构成对象的两个最重要的因素就是特征和动作。每一个现实世界中的实体都具有不同的特征和动
2、作。 在编程领域,编程开发人员需要尽可能地用对象来模拟现实世界的实体,因此,在程序里,对象具有状态和行为。对象的状态就是现实世界中对应实体的属性特征,对象的行为就是现实世界中对应实体能够执行的动作。 在 C#中,对象的状态被分别存储在一些变量里,而对象的行为,则通过定义方法来实施。方法的作用就是要完成一项特殊的工作。,第4章 面向对象的编程基础,知识点,对象:明白对象的属性和方法,学会使用对象。 类:辨别对象和类,明白类的属性和方法,了解类的成员。 属性:学会在类中添加属性,保护数据安全,以及封装字段。 结构:如何创建结构,结构和类有什么区别? 索引器:会使用索引器,4.1 类,类与对象 类:
3、一组具有相同数据结构和相同操作的对象的集合。例如:汽车 对象(实例):类的实例化 。例如:卡车 注意:要使用对象,必须先定义类,然后再创建对象。 对象的生存周期 对象在建立时分配了内存,创建对象实际上作了两个方面的工作: (1)使用new保留字要求系统分配内存; (2)使用构造函数初始化数据。 销毁对象也是做了两个方面的工作:(1)释放占用的内存;(2)将分配给对象的内存归还给堆(Heap)。,4.1 类的组织,1.类的声明 用class定义类,声明类的形式为: 附加声明 访问修饰符 class 类名称:基类 ,接口序列 字段声明构造函数方法事件 ,注意:中的内容可省或任选其一,当两者都有时,
4、先基类后接口,4.1 类的组织(续),public class Childprivate int age;private string name;/ 不带参数的构造函数public Child()name = “none“;/ 带参数的构造函数public Child(string name, int age),如果不指明,则 默认为internal,4.1 类的组织(续),this.name = name;this.age = age;/ 输出方法public void PrintChild()Console.WriteLine(“0, 1 years old.“, name, age);
5、public class Program ,4.1 类的组织(续),public static void Main()/使用new关键字创建对象,new后是调用的构造函数Child child1 = new Child(“Zhang San“, 11);Child child2 = new Child(“Li Si“, 10);Child child3 = new Child();/ 显示结果Console.Write(“Child #1: “);child1.PrintChild();Console.Write(“Child #2: “);child2.PrintChild();Consol
6、e.Write(“Child #3: “);child3.PrintChild();,4.1 类的组织(续),Console.ReadLine(); 输出结果: Child #1: Zhang San, 11 years old. Child #2: Li Si, 10 years old. Child #3: none, 0 years old.,c#中this的用法,1、表示对象的当前实例。如果实例成员具有与形参相同的名称,则可以这样使用以示区别。 2、将当前对象作为形参传递到其他方法中 class Employee private string name;private string a
7、lias;private decimal salary = 3000.00m;/ Constructor:public Employee(string name, string alias)/ Use this to qualify the fields, name and alias:this.name = name;this.alias = alias;,/ Printing method:public void printEmployee()Console.WriteLine(“Name: 0nAlias: 1“, name, alias);/ Passing the object to
8、 the CalcTax method by using this:Console.WriteLine(“Taxes: 0:C“, Tax.CalcTax(this);public decimal Salaryget return salary; class Tax public static decimal CalcTax(Employee E)return 0.08m * E.Salary; 3、声明索引器 。由于静态成员函数存在于类一级,并且不是对象的一部分,因此没有 this 指针。在静态方法中引用 this 是错误的。,c#中this的用法,静态类,类可以声明为 static 的,以
9、指示它仅包含静态成员。不能使用 new 关键字创建静态类的实例。静态类在加载包含该类的程序或命名空间时由 .NET Framework 公共语言运行库 (CLR) 自动加载。 使用静态类来包含不与特定对象关联的方法。例如,创建一组不操作实例数据并且不与代码中的特定对象关联的方法,这种情况下应该使用静态类来包含那些方法。 静态类的主要功能如下: 它们仅包含静态成员。 它们不能被实例化。 它们是密封的。 它们不能包含实例构造函数(C# 编程指南)。,使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员。编译器将保证不会创建此类的实例。 静态类是密封的,因此不可被继承。静态类不能包含
10、构造函数,但仍可声明静态构造函数以分配初始值或设置某个静态状态。 使用静态类作为不与特定对象关联的方法的组织单元。此外,静态类能够使实现更简单、迅速,因为不必创建对象就能调用其方法。以一种有意义的方式组织类内部的方法(例如 System 命名空间中的 Math 类的方法)是很有用的。,静态类,static class CompanyInfo public static string GetCompanyName() return “CompanyName“; public static string GetCompanyAddress() return “CompanyAddress“; /.
11、,静态类,4.1.2 构造函数,作用:构造函数是一个特殊的方法,用于在建立对象时 为对象分配存储空间,对数据成员进行初始化。好处:确保每一个对象在被使用之前都适当地进行了初始化的动作。构造函数特点: 1) 每个类至少有一个构造函数。若程序代码中没有构造函数则系统会自动提供一个默认的构造函数。2) 一个构造函数总是和它的类名相同。3) 构造函数不包含任何返回值。4) 构造函数总是public的。一般在构造函数中作初始化工作,对于执行过程用时比较长的程序代码,最好不要放在构造函数中。5)必须使用操作符 new 来调用一个构造函数,4.1.2 构造函数(续),1、默认构造函数 (1)如果在类中不定义
12、构造函数,系统会提供一个默认的构造函数。 (2)默认构造函数没有参数。 (3)默认构造函数自动将非静态成员初始化为:数值型:如int、double等,初始化为0。bool类型:初始化为false.引用类型:初始化为null。 (4)如果自己定义了类的构造函数,则所有初始化工作由编程者自己完成。,4.1.2 构造函数(续),2. 重载构造函数有时候可能会遇到这样的情况:在一个类中的多个方法中都要用到某一个数据成员,而该成员值必须从其他类中传递过来。这时,无参数的构造函数就不能胜任了,解决这个问题最好的办法就是:重载(Overloading)构造函数。,4.1.2 构造函数(续),using Sy
13、stem; using System.Collections.Generic; using System.Text; namespace OverloadingExample class Programpublic Program()Console.WriteLine(“null“);,4.1.2 构造函数(续),public Program(string str)Console.WriteLine(str);static void Main()Program aa = new Program();Program bb = new Program(“How are you!“);Console
14、.ReadLine(); ,复制构造函数某些编程语言的类提供了复制构造函数,即从当前类实例构造一个新的类实例。在C#编程语言中,没有提供复制构造函数,如果要实现复制构造的功能,就必须自行编写这样的构造函数。C#复制构造函数仍然属于构造函数,不同的是以类本身作为参数的数据类型。调用复制构造函数时,参数必须是已经创建的类实例。 Contact c1 = new Contact(“lisi“); Contact c2 = new Contact(c1);,4.1.2 构造函数(续),4.1.3 字段和局部变量,字段:指声明为类一级的对象或值类型的变量。 局部变量:指在方法、事件以及构造函数内声明的变
15、量。 public class Programpublic static int j=20; /字段public static void Main()int j=30; /局部变量Console.WriteLine(j); /输出结果:30Console.WriteLine(Program.j); /输出结果:20Console.ReadLine();,4.1.3 字段和局部变量,当字段和局部变量名相同时,如果要引用静态字段,可以使用下面的形式:类名.字段名 如果是实例字段,则使用下面的形式:this.字段名 这里的this指当前实例。当然,如果没有出现字段和局部变量名重名的情况下,引用字段的
16、形式和引用局部变量的形式相同。,4.1.4 静态成员与实例成员,类的成员包括字段、属性、构造函数、方法、事件、索引、嵌套类。 类的成员分静态成员和实例成员 静态成员在内存中只有一份 静态成员要等到应用程序结束时才会退出内存。 把只有创建了类的实例才能够使用的成员叫实例成员。 静态成员作用: 1、需要在一个类的各个对象间交互,即需要一个数据对象为整个类而非某个对象服务。 2、同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。 类的static成员具有如下特征:有独立的存储区,属于整个类。,注意:1、对于静态的数据成员,连接器会保证它拥有一个单一的外部定义。静态数据成员按定义出现
17、的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。2、类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。,4.1.4 静态成员与实例成员,4.1.5 访问修饰符,C#中有以下成员访问修饰符: Public (常用)任何外部的类都可以不受限制的存取这个类的方法和数据成员。 private (常用)类中的所有方法和数据成员只能在此类中使用,外部无法存取。(默认) Protected 除了让本身的类可以使用之外,任何继承自此类的子类都可以存取。 Internal 在当前项目中都可以存
18、取。该访问权限一般用于基于组件的开发,因为它可以使组件以私有方式工作,而该项目外的其它代码无法访问。类的访问修饰符默认为Internal Protected internal 只限于当前项目,或者从该项目的类继承的类才可以存取。 Partial 局部类型,类的定义和实现可以分布在多个文件中,但都要使用partial标注,基类只需要声明一次,若多次声明则必须完全一致。,命名空间隐式地具有 public 已声明可访问性。在命名空间声明中不允许使用访问修饰符。 编译单元或命名空间中声明的类型可以具有 public 或 internal 已声明可访问性,默认的已声明可访问性为 internal。 类成
19、员可具有五种已声明可访问性中的任何一种,默认为 private 已声明可访问性。(请注意,声明为类成员的类型可具有五种已声明可访问性中的任何一种,而声明为命名空间成员的类型只能具有 public 或 internal 已声明可访问性。) 枚举成员隐式地具有 public 已声明可访问性。在枚举成员声明中不允许使用访问修饰符。,4.1.5 访问修饰符,结构成员可以具有 public、internal 或 private 已声明可访问性并默认为 private 已声明可访问性,这是因为结构是隐式地密封的。结构的成员若是在此结构中声明的(也就是说,不是由该结构从它的基类中继承的)不能具有 prote
20、cted 或 protected internal 已声明可访问性。(请注意,声明为结构成员的类型可具有 public、internal 或 private 已声明可访问性,而声明为命名空间成员的类型只能具有 public 或 internal 已声明可访问性。) 接口成员隐式地具有 public 已声明可访问性。在接口成员声明中不允许使用访问修饰符。,4.1.5 访问修饰符,局部类型partial,C# 2.0 引入了局部类型的概念。局部类型允许我们将一个类、结构或接口分成几个部分,分别实现在几个不同的.cs文件中。 局部类型适用于以下情况: (1) 类型特别大,不宜放在一个文件中实现。 (
21、2) 一个类型中的一部分代码为自动化工具生成的代码,不宜与我们自己编写的代码混合在一起。 (3) 需要多人合作编写一个类。 局部类型是一个纯语言层的编译处理,不影响任何执行机制事实上C#编译器在编译的时候仍会将各个部分的局部类型合并成一个完整的类。,2. 局部类型的限制 (1) 局部类型只适用于类、接口、结构,不支持委托和枚举。 (2) 同一个类型的各个部分必须都有修饰符 partial。 (3) 使用局部类型时,一个类型的各个部分必须位于相同的命名空间中。 (4) 一个类型的各个部分必须被同时编译。,局部类型partial,partial,3. 局部类型的注意点 (1) 关键字partial
22、是一个上下文关键字,只有和 class、struct、interface 放在一起时才有关键字的含义。因此partial的引入不会影响现有代码中名称为partial的变量。 (2) 局部类型的各个部分一般是分开放在几个不同的.cs文件中,但C#编译器允许我们将他们放在同一文件中。,4.2 结构,结构(struct)是由一系列相关的、但类型不一定相同的变量组织在一起而构成的数据表示形式,所有结构类型都隐式地从类型object继承。通常用来封装小型相关变量组,例如,矩形的坐标或库存商品的特征。 例如:public struct Point public int x;public int y;pub
23、lic string s; C#中的结构:DateTime/TimeSpan/Point/Int32/Double等,在结构声明中,除非字段被声明为 const 或 static,否则无法初始化。 结构不能声明默认构造函数(没有参数的构造函数)或析构函数。 结构在赋值时进行复制。 将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据。 凡是定义为结构的,都可以用类来定义。 创建轻量级对象时,可以使用结构。,4.2 结构,结构是值类型,而类是引用类型。 与类不同,结构的实例化可以不使用 new 运算符。 结构可以声明带参数的构造函数。 一个结构不能从另一个结构或
24、类继承,而且不能作为一个类的基。 结构可以实现接口。 结构可用作可以为 null 的类型,因而可向其赋 null 值。 结构不能包含显式的无参数构造函数。 Class的构造函数不需要初始化全部字段,Struct的构造函数必须初始化所有字段,4.2 结构,结构,结构,结构,使用场合: (1)实现一个主要用于底层存储数据类型的结构时,可以考虑结构。将用于定义应用程序行为的类型设计为Class。 (2)Struct变量占用堆栈的空间,因此适用于数据量相对小的场合,堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些。 (3)结构数组具有更高的效率 (4)结构表示如点,矩形和颜色这样轻量的对
25、象,例如,如果声明一个含有1000个点的对象数组,则将为引用每个对象分配附加的内存,在此情况下,结构的成本较低。 (5) 在表现抽象和多级别的对象层次时,类是最好的选择,4.3 方法,方法(Method)是一组程序代码的集合,每个方法都有一个方法名,便于识别和让其他方法调用。 1. 方法的定义与使用(1)方法必须放在某个类中。(2)定义方法的语法形式为:访问修饰符 返回值类型 方法名称(参数序列)语句序列 ,4.3 方法(续),定义方法时,需要注意以下几点: 方法名不能和变量、常数或者任何声明在类中其它的成员相同。 方法可以有参数,也可以没有参数,但是不论是否有参数,小括号都是必需的。如果参数
26、序列中的参数有多个,则以逗号分开。 结束某个方法的执行,可以使用return语句,程序遇到return语句后,会将执行流程交还给调用此方法的程序代码段。此外,还可以用return语句返回一个值。 如果声明一个非void类型的方法,则方法中必须至少有一个return语句。,4.3 方法(续),using System; class Programpublic int MyMethod() Console.WriteLine(“this is MyMethod.“);int i = 10;return i;static void Main()Program method = new Program
27、();int j = 5;j = method.MyMethod();Console.WriteLine(“the value is 0.“, j);,2. 方法中的参数传递 1) 传递值类型的参数 形参(parameter):表示定义方法的参数 实参(argument):表示实际用于调用方法的参数 C#中所有的实参默认按值传递,换句话说,被调用方法获得传入的实参值的副本。值类型参数的格式为: 参数类型 参数名,4.3 方法,4.3 方法,class Program public static void AddOne(int x)x+;static void Main()int a = 3;C
28、onsole.WriteLine(“调用AddOne之前,a=0“, a);AddOne(a);Console.WriteLine(“调用AddOne之后,a=0“, a);Console.ReadLine(); ,4.3 方法(续),2) 传递引用类型的参数 引用类型参数的格式为: ref 参数类型 参数 如果在方法中改动按引用传入的变量,则会修改这些变量的值。不能将字面值作为实参传入要求按引用传入形参的方法。 class Program public static void AddOne(ref int x)x+;static void Main()int a = 3;Console.Wr
29、iteLine(“调用AddOne之前,a=0“, a);AddOne(ref a);Console.WriteLine(“调用AddOne之后,a=0“, a);Console.ReadLine();,4.3 方法(续),3) 输出多个引用类型的参数 输出引用类型参数的格式为: out 参数类型 参数名 目的是完全使用变量获得来自于方法的一些返回值。 class Programpublic static void MyMethod(out int a, out int b) a = 5;b = 6;static void Main() int x, y;MyMethod(out x, out
30、 y);Console.WriteLine(“调用MyMethod之后,x=0,y=1“, x, y);Console.ReadLine();,Ref参数传递与Out参数传递异同点,相同点:都是传递引用类型的参数,编译器处理时没有区别 不同点: Ref参数使用前必须要初始化 Out参数使用前不必初始化 注意: public void AddOne(ref int x) public void AddOne(out int x)上面这两个方法,不是重载方法。,4.3 方法(续),4) 传递个数不确定的参数 需要传递的参数个数不确定时,可以采用params关键字,其后一般跟一个数组。例如: cla
31、ss Programpublic static float Average(params long v)long total, i;for (i = 0, total = 0; i v.Length; +i)total += vi;return (float)total / v.Length;,4.3 方法(续),static void Main()float x = Average(1, 2, 3, 5);Console.WriteLine(“1、2、3、5的平均值为0“, x);x = Average(4, 5, 6, 7, 8);Console.WriteLine(“4、5、6、7、8的
32、平均值为0“, x);Console.ReadLine(); ,4.3 方法(续),3. 方法重载方法重载是指具有相同的方法名,但参数类型或参数个数 不完全相同的多个方法可以同时出现在一个类中。 using System; class Program public static int Add(int i, int j)return i + j;public static string Add(string s1, string s2)return s1 + s2;public static long Add(long x)return x + 5;,4.3 方法(续),static void
33、 Main()Console.WriteLine(Add(1, 2);Console.WriteLine(Add(“1“, “2“);Console.WriteLine(Add(10);/按回车键结束Console.ReadLine();,4.4 属性与索引器,属性的作用:可以限制外部对类中成员变量的存取权限。定义一个属性时,类型必须与其所访问的字段类型一致。属性为读/写私有数据成员的值提供简便的方法访问。 1.属性的声明和定义get:用来读取数据成员的值。set:用来设置数据成员的值。 public class MyClass private int number = 0;public in
34、t MyNumber getreturn number;setif (value 0) number = value; /value是关键字,其值由编译器自动生成,包含了set存取器赋予的值。 ,4.4.1 属性(续),public class Programpublic static void Main()MyClass me = new MyClass(); Console.WriteLine(me.MyNumber);me.MyNumber = 5;Console.WriteLine(me.MyNumber);Console.ReadLine(); ,/MyClass.cs using
35、System; namespace ConsoleTest class MyClassprivate int number;public int Numbergetreturn number;setif (value 0)number = value; ,/Program.cs using System; namespace ConsoleTest public class Programpublic static void Main()MyClass me = new MyClass();me.Number = -1;Console.WriteLine(“Number:0“, me.Numb
36、er);Console.ReadLine(); 回答:Number输出的值是多少?,封装有如下好处: 避免使用非法数据赋值。 保证数据的完整性。 避免类内部发生修改时,导致整个程序的修改。,4.4.1 属性(续),快速创建属性属性在 C#开发中使用的非常普遍的,除了手动输入代码以外,VS 还提供了一个快捷的方法:重构封装字段。在一个类中,定义一个字段,通常设定为私有,选中这个字段并右击,在弹出的书生菜单中选择【重构】【封装字段命令,如下页左图所示。封装结束后就会弹出一个窗口,可以修改属性名,单击【确定】按钮就会将选择的字段与属性关联,如下页右图所示。确定后创建和选定字段关联的属性,也可以称为将
37、字段封装。,4.4.1 属性(续),4.4.1 属性(续),我们知道数组中的元素可以通过索引进行检索,如果一个类中有很多对象,怎样简单快速地查找到需要的对象以及对象的特性呢?索引器就是为了实现这样的功能而设计的。索引器允许类或者结构的实例可以按照数组那样进行索引。可以简单地把索引器理解为常见的书中的目录,字典中提供检索的索引。 您查过字典吗?这个问题太幼稚了,相信大家都查过字典,无论新华字典还是成语词典都提供了检索方法,可以根据拼音检索,也可根据汉字部首检索,当然还有其他检索方式,帮助我们快速查找需要的信息。这节学习的索引器类似于字典中的检索,可以根据需要设定不同的检索方式,以快速查找类或结构
38、的实例。,4.4.2 索引器,4.4.2 索引器,索引器用于封装内部集合或数组。 索引器在语法上方便了程序员将类、结构或接口作为数组进行访问。 要声明类或结构上的索引器,需要使用this关键字。 索引器的定义类似于属性,但其功能与属性并不相同。索引器提供一种特殊的方法编写get和set访问器。属性可以使用户像访问字段一样访问对象的数据,索引器可以使用户像访问数组一样访问类成员。定义索引器的语法如下:,索引器是一种特殊的类成员,它能够让类、结构和接口的实例按照与数组相同的方式进行索引,使程序看起来更为直观,更容易编写。 1、索引器的定义C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了
39、数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作。定义索引器的方式与定义属性有些类似,其一般形式如下:修饰符 数据类型 this索引类型 index get/获得属性的代码 set /设置属性的代码,4.4.2 索引器,修饰符包括 public,protected,private,internal,new,virtual,sealed,override, abstract,extern. 数据类型是表示将要存取的数组或集合元素的类型。 索引器类型表示该索引器使用哪一类型的索引来存取数组或集合元素,可以是整数,可以是字符串;this表示操作本对象的数组或集合成员,可以简单把它理解成索
40、引器的名字,因此索引器不能具有用户定义的名称。 例如:,4.4.2 索引器,class demo /可容纳100个整数的整数集 private long arr = new long100; /声明索引器 public long thisint index get /检查索引范围 if (index = 100) return 0; else return arrindex; set if (!(index = 100) arrindex = value; ,接口中的索引器,在接口中也可以声明索引器,接口索引器与类索引器的区别有两个:一是接口索引器不使用修饰符;二是接口索引器只包含访问器get
41、或set,没有实现语句。访问器的用途是指示索引器是可读写、只读还是只写的,如果是可读写的,访问器get或set均不能省略;如果只读的,省略set访问器;如果是只写的,省略get访问器。,例如: public interface IAddress string thisint indexget;set; string Addressget;set; string Answer(); 表示所声明的接口IAddress包含3个成员:一个索引器、一个属性和一个方法,其中,索引器是可读写的。,接口中的索引器,例1: public int thisint index / 索引器声明 / get and s
42、et accessors 例2: public string thisint i getreturn namei;setnamei=value; ,4.4.2 索引器,索引器与属性的比较,索引器与属性都是类的成员,语法上非常相似。索引器一般用在自定义的集合类中,通过使用索引器来操作集合对象就如同使用数组一样简单;而属性可用于任何自定义类,它增强了类的字段成员的灵活性。,索引器与属性,4.4.2 索引器,【例4-12】索引器的声明和用法。,本章相关面试题选择题,1.以下关于ref和out的描述哪些项是正确的?(多选) A.使用ref参数,传递到ref参数的参数必须最先初始化。 B.使用out参数
43、,传递到out参数的参数必须最先初始化。 C.使用ref参数,必须将参数作为ref参数显式传递到方法。 D.使用out参数,必须将参数作为out参数显式传递到方法。 A C D 2.下列关于构造函数的描述正确的是(单选) a)构造函数可以声明返回类型。 b)构造函数不可以用private修饰 c)构造函数必须与类名相同 d)构造函数不能带参数C,读程序写出运行结果,class Class1 private string str = “Class1.str“; private int i = 0; static void StringConvert(string str) str = “stri
44、ng being converted.“; static void StringConvert(Class1 c) c.str = “string being converted.“; static void Add(int i) i+; static void AddWithRef(ref int i) i+; ,static void Main() int i1 = 10; int i2 = 20; string str = “str“; Class1 c = new Class1(); Add(i1); AddWithRef(ref i2); Add(c.i); StringConver
45、t(str); StringConvert(c); Console.WriteLine(i1); Console.WriteLine(i2); Console.WriteLine(c.i); Console.WriteLine(str); Console.WriteLine(c.str); ,运行结果为:,10210strstring being converted.,结构和类的区别:,请详述在.NET中类(class)与结构(struct)的异同(某IT公司),答: 结构不能被另外一个结构或者类继承,自身也不能被继承。而类可以被其他类继承,其自身也能被继承 结构没有默认的构造函数,也没有析构函数,并且不能有protected修饰符,但可以不使用new进行初始化。而类有默认的构造函数,也有析构函数,还有protected修饰符,并且必须使用new进行初始化。 结构是值类型,而类是引用类型。,本章作业,P79页 1、2、4继续完成实验二,