6.面向对象-封装-构造函数和析构函数
6.1 知识点
构造函数
基本概念
- 在实例化对象时,会调用用于初始化的函数。
- 如果不写,默认存在一个无参构造函数。
构造函数的写法
- 没有返回值。
- 函数名和类名必须相同。
- 一般为
public
,除非有特殊需求。 - 构造函数可以被重载。
this
代表当前调用该函数的对象自己。
注意:
- 如果不自己实现无参构造函数而实现了有参构造函数,会失去默认的无参构造。
声明人类
class Person
{
public string name;
public int age;
// 人类内部
}
人类内部
public Person()
{
name = "小林";
age = 18;
}
public Person(int personAge)
{
age = personAge;
}
public Person(string name)
{
// this 代表当前调用该函数的对象自己
// 有参构造函数参数名和自己成员变量名一样时使用 this
this.name = name;
}
主函数内
Person p1 = new Person(); // 无参构造函数
Person p2 = new Person(20); // 有参构造函数
Person p3 = new Person("林浩僧");// 有参构造函数重载
构造函数特殊写法
- 可以通过
this
重用构造函数代码。 - 访问修饰符 构造函数名(参数列表):this(参数1,参数2….)。
- 这样的话会先调用
this
后面重载的构造函数,再返回当前的构造函数。 - 后面重载的构造函数的参数可以传任意对应的类型。
人类内部
public Person(int age, string name):this(age + 10)
{
Console.WriteLine("Person两个参数构造函数调用");
}
主函数内
Person p4 = new Person(18, "林文韬");
Console.WriteLine(p4.age); // 28
// 通过 public Person(int age, string name): this(age + 10)
// 重用构造函数代码,age比传进去的年龄+了10
析构函数
基本概念
- 当引用类型的堆内存被回收时,会调用该函数。
- 在C#中,由于存在自动垃圾回收机制(GC),极少需要使用析构函数。
注意:
- 在Unity开发中析构函数几乎不会使用,因此了解即可。
基本语法
// ~类名()
// {
// }
人类内部
// 析构函数是当引用类型的堆内存(垃圾)真正被回收时才会调用的函数
~Person()
{
}
主函数内
p4 = null;
// 设置为 null 后,引用类型的堆内存虽然变成了垃圾,但是没有立即触发垃圾回收
垃圾回收机制
基本概念
- 垃圾回收,英文简写GC(Garbage Collector)。
- 过程是在遍历堆(Heap)上动态分配的所有对象,通过识别是否被引用来确定哪些对象是垃圾,哪些对象仍需被使用。
- 垃圾即没有被任何变量或对象引用的内容,需要被回收释放。
垃圾回收算法
- 引用计数(Reference Counting)
- 标记清除(Mark Sweep)
- 标记整理(Mark Compact)
- 复制集合(Copy Collection)
注意:
- GC只负责堆(Heap)内存的垃圾回收。
- 引用类型都存在堆(Heap)中,因此它们的分配和释放都通过垃圾回收机制来管理。
- 栈(Stack)上的内存是由系统自动管理的。
- 值类型在栈(Stack)中分配内存,有自己的生命周期,不需要手动管理,会自动分配和释放。
C#中内存回收机制的大概原理
- 垃圾会被分为 0代内存、1代内存、2代内存。
- 代是垃圾回收机制使用的一种算法(分代算法)。
- 新分配的对象都会被配置在第0代内存中。
- 在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,进行以下两步:
- 标记对象:从根(静态字段、方法参数)开始检查引用对象,标记为可达对象,未标记为不可达对象,不可达对象即为垃圾。
- 搬迁对象压缩堆:释放未标记的对象,搬迁可达对象,并修改引用地址。
- 大对象总被认为是第二代内存,目的是减少性能损耗,提高性能。
- 不会对大对象进行搬迁压缩,85000字节(83kb)以上的对象为大对象。
主函数内
// 手动触发垃圾回收的方法
// 一般情况下,不会频繁调用
// 通常在 Loading 过场景时才调用
GC.Collect();
总结
构造函数
- 实例化对象时调用的函数,主要用于初始化成员变量。
- 基本语法:
- 不写返回值。
- 函数名和类名相同。
- 访问修饰符根据需求而定,一般为
public
。
- 注意:
- 可以重载构造函数。
- 可以用
this
语法重用代码。 - 可以在函数中用
this
区分同名参数和成员变量。 - 有参构造会顶掉默认的无参构造。
- 基本语法:
析构函数
- 当对象被垃圾回收时调用,主要用于回收资源或特殊处理内存。
- 基本语法:
- 不写返回值。
- 不写修饰符。
- 不能有参数。
- 函数名和类名相同,前面加
~
.
- 基本语法:
垃圾回收机制
- 垃圾回收的过程是在遍历堆上动态分配的所有对象,通过识别是否被引用来确定哪些对象是垃圾。
- 引用类型的分配和释放通过垃圾回收机制管理。
- C#中的垃圾回收算法有多种,通常采用分代算法。
- GC只负责堆内存的垃圾回收,栈上的内存由系统自动管理。
- 值类型在栈上分配内存,有自己的生命周期,不需要手动管理。
6.2 知识点代码
using System;
namespace Lesson4_封装_构造函数和析构函数
{
#region 人 类
//声明 人 类
class Person
{
public string name;
public int age;
//人 类 内部
#region 知识点一 构造函数
//类中是允许自己申明无参构造函数的
//结构体是不允许 必须申明有参构造函数的
//无参构造函数
//不声明任何构造函数的话 默认就有无参构造函数
//声明有参构造函数但是不声明无参构造函数的话 默认的无参构造函数会被代替掉
public Person()
{
name = "小林";
age = 18;
}
//有参构造函数
public Person(int personAge)
{
age = personAge;
}
//有参构造函数
public Person(string name)
{
//this代表当前调用该函数的对象自己
//有参构造函数参数名和自己成员变量名一样时 使用this
this.name = name;
}
#endregion
#region 知识点二 构造函数特殊写法
//可以通过this 重用构造函数代码
//访问修饰符 构造函数名(参数列表):this(参数1,参数2....)
//这样的话会先调用this后面重载的构造函数 在返回当前的构造函数
//后面重载的构造函数的参数可以传任意对应的类型,写死也行
public Person(int age, string name) : this(age + 10)
{
Console.WriteLine("Person两个参数构造函数调用");
}
#endregion
#region 知识点三 析构函数
//析构函数 是当引用类型的堆内存(垃圾)真正被回收时 才会调用的函数
~Person()
{
}
#endregion
}
#endregion
#region 知识点一 构造函数
//基本概念
//在实例化对象时 会调用的用于初始化的函数
//如果不写 默认存在一个无参构造函数
//构造函数的写法
//1.没有返回值
//2.函数名和类名必须相同
//3.没有特殊需求时 一般都是public的
//4.构造函数可以被重载
//5.this代表当前调用该函数的对象自己
//注意:
// 如果不自己实现无参构造函数而实现了有参构造函数
// 会失去默认的无参构造
#endregion
#region 知识点二 构造函数特殊写法
//可以通过this 重用构造函数代码
//访问修饰符 构造函数名(参数列表):this(参数1,参数2....)
//这样的话会先调用this后面重载的构造函数 在返回当前的构造函数
//后面重载的构造函数的参数可以传任意对应的类型,写死也行
#endregion
#region 知识点三 析构函数
//基本概念
//当引用类型的堆内存被回收时,会调用该函数
//对于需要手动管理内存的语言(比如C++),需要在析构函数中做一些内存回收处理
//但是C#中存在自动垃圾回收机制GC
//所以我们几乎不会怎么使用析构函数。除非你想在某一个对象被垃圾回收时,做一些特殊处理
//注意:
//在Unity开发中析构函数几乎不会使用,所以该知识点只做了解即可
//基本语法
// ~类名()
// {
// }
#endregion
#region 知识点四 垃圾回收机制
//基本概念
//垃圾回收,英文简写GC(Garbage Collector)
//垃圾回收的过程是在遍历堆(Heap)上动态分配的所有对象
//通过识别它们是否被引用来确定哪些对象是垃圾,哪些对象仍要被使用
//所谓的垃圾就是没有被任何变量,对象引用的内容
//垃圾就需要被回收释放
//垃圾回收算法
//垃圾回收有很多种算法,比如
//引用计数(Reference Counting)
//标记清除(Mark Sweep)
//标记整理(Mark Compact)
//复制集合(Copy Collection)
//注意:
//GC只负责堆(Heap)内存的垃圾回收
//引用类型都是存在堆(Heap)中的,所以它的分配和释放都通过垃圾回收机制来管理
//栈(Stack)上的内存是由系统自动管理的
//值类型在栈(Stack)中分配内存的,他们有自己的生命周期,不用对他们进行管理,会自动分配和释放
//C#中内存回收机制的大概原理
//垃圾会被分为 0代内存 1代内存 2代内存
//代的概念:
//代是垃圾回收机制使用的一种算法(分代算法)
//新分配的对象都会被配置在第0代内存中
//每次分配都可能会进行垃圾回收以释放内存(0代内存满时)
//在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行以下两步
//1.标记对象 从根(静态字段、方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象
// 不可达对象就认为是垃圾
//2.搬迁对象压缩堆(比如不是垃圾的0代会搬到1代去) (挂起执行托管代码线程) 释放未标记的对象 搬迁可达对象 修改引用地址
//大对象总被认为是第二代内存 目的是减少性能损耗,提高性能
//不会对大对象进行搬迁压缩 85000字节(83kb)以上的对象为大对象
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("构造函数和析构函数");
//主函数内
#region 知识点一 构造函数
Person p1 = new Person();//无参构造函数
Person p2 = new Person(20);//有参构造函数
Person p3 = new Person("林浩僧");//有参构造函数重载
#endregion
#region 知识点二 构造函数特殊写法
Person p4 = new Person(18,"林文韬");
Console.WriteLine(p4.age);//28
//通过public Person(int age, string name) : this(age + 10)
//重用构造函数代码 age比传进去的年龄+了10
#endregion
#region 知识点三 析构函数
p4 = null;
//设置为null后 引用类型的堆内存虽然变成了垃圾 但是没有立即触发垃圾回收
#endregion
#region 知识点四 垃圾回收机制
//手动触发垃圾回收的方法
//一般情况下 我们不会频繁调用
//都是在 Loading过场景时 才调用
GC.Collect();
#endregion
}
}
//总结
//构造函数
//实例化对象时 调用的函数
//主要是用来初始化成员变量的
//基本语法
//不写返回值
//函数名和类名相同
//访问修饰符根据需求而定
//一般为public
//注意
//可以重载构造函数
//可以用this语法重用代码
//可以在函数中用this区分同名参数和成员变量
//有参构造会顶掉默认的无参构造
//析构函数
//当对象呗垃圾回收时 调用的,主要是用来回收资源或者特殊处理内存的
//基本语法
//不写返回值
//不写修饰符
//不能有参数
//函数名和类名相同
//前面加~
}
6.3 练习题
基于成员方法练习题,对人类的构造函数进行重载,用人类创建若干个对象
class语句块外 namespace语句块内
// class语句块外 namespace语句块内
// 人 类
class Person
{
public string name;
public float height;
public int age;
public string homeAddress;
//有参数构造函数
public Person(string name, float height)
{
this.name = name;
this.height = height;
}
//有参数构造函数 特殊写法
public Person(string name, float height, int age, string homeAddress) : this(name, height)
{
//this
this.age = age;
this.homeAddress = homeAddress;
}
//成员方法
public void Speak(string str)
{
Console.WriteLine("{0}说{1}", name, str);
}
public void Walk()
{
Console.WriteLine("{0}开始走路了", name);
}
public void Eat(Food f)
{
Console.WriteLine("{0}吃了{1}", name, f.name);
}
}
// 食物 类
class Food
{
public string name;
public int kaluli;
}
主函数内
// 主函数内
Person p1 = new Person("林文韬", 177.5f, 18, "成都");
Person p2 = new Person("小火", 184.5f);
//Person p3 = new Person();//写了有参构造函数 默认无参构造函数被顶掉 会报错
基于成员变量练习题,对班级类的构造函数进行重载,用班级类创建若干个对象
class语句块外 namespace语句块内
// class语句块外 namespace语句块内
// 班级 类
class Class
{
public string name;
public int capacity;
public Student[] students;
//有参数构造函数
public Class(string name, int capacity)
{
this.name = name;
this.capacity = capacity;
}
//有参数构造函数 特殊写法
public Class(string name, int capacity, Student[] students) : this(name, capacity)
{
this.students = students;
}
}
// 学生 类
class Student
{
}
主函数内
// 主函数内
Class c1 = new Class("Unity", 9999999);
Class c2 = new Class("C#", 9999999, new Student[] { new Student(), new Student() });
写一个Ticket类,有一个距离变量(在构造对象时赋值,不能为负数),有一个价格特征,有一个方法GetPrice可以读取到价格,并且根据距离distance计算价格price(1元/公里)
- 0~100公里 不打折
- 101~200公里 大9.5折
- 201~300公里 打9折
- 300公里以上 打8折
- 有一个显示方法,可以显示这张票的信息。
- 例如:100公里100块钱
class语句块外 namespace语句块内
// class语句块外 namespace语句块内
// 票 类
class Ticket
{
public uint distance;
public float price;
//有参数构造函数
public Ticket(uint distance)
{
this.distance = distance;
price = GetPrice();
}
//成员方法
//获得价格
private float GetPrice()
{
//0~100公里 不打折
//101~200公里 打9.5折
//201~300公里 打9折
//300公里以上 打8折
if (distance > 300)
{
return distance * 0.8f;
}
else if (distance >= 201 && distance <= 300)
{
return distance * 0.9f;
}
else if (distance >= 101 && distance <= 200)
{
return distance * 0.95f;
}
else
{
return distance;
}
}
//展示信息
public void ShowInfo()
{
float perKMprice = price / distance;
Console.WriteLine("{0}公里{1}块钱", distance, price);
Console.WriteLine("平均每公里价格是" + perKMprice + "块钱");
}
}
主函数内
// 主函数内
Ticket t = new Ticket(1000);
t.ShowInfo();
//1000公里800块钱
//平均每公里价格是0.8块钱
6.4 练习题代码
using System;
namespace Lesson4_练习题
{
//class语句块外 namespace语句块内
#region 练习题一
//基于成员方法练习题
//对人类的构造函数进行重载,用人类创建若干个对象
// 人 类
class Person
{
public string name;
public float height;
public int age;
public string homeAddress;
//有参数构造函数
public Person(string name, float height)
{
this.name = name;
this.height = height;
}
//有参数构造函数 特殊写法
public Person(string name, float height, int age, string homeAddress) : this(name, height)
{
//this
this.age = age;
this.homeAddress = homeAddress;
}
//成员方法
public void Speak(string str)
{
Console.WriteLine("{0}说{1}", name, str);
}
public void Walk()
{
Console.WriteLine("{0}开始走路了", name);
}
public void Eat(Food f)
{
Console.WriteLine("{0}吃了{1}", name, f.name);
}
}
// 食物 类
class Food
{
public string name;
public int kaluli;
}
#endregion
#region 练习题二
//基于成员变量练习题
//对班级类的构造函数进行重载,用班级类创建若干个对象
// 班级 类
class Class
{
public string name;
public int capacity;
public Student[] students;
//有参数构造函数
public Class(string name, int capacity)
{
this.name = name;
this.capacity = capacity;
}
//有参数构造函数 特殊写法
public Class(string name, int capacity, Student[] students):this(name, capacity)
{
this.students = students;
}
}
// 学生 类
class Student
{
}
#endregion
#region 练习题三
//写一个Ticket类,有一个距离变量(在构造对象时赋值,不能为负数),有一个价格特征,
//有一个方法GetPrice可以读取到价格,并且根据距离distance计算价格price(1元/公里)
//0~100公里 不打折
//101~200公里 打9.5折
//201~300公里 打9折
//300公里以上 打8折
//有一个显示方法,可以显示这张票的信息。
//例如:100公里100块钱
// 票 类
class Ticket
{
public uint distance;
public float price;
//有参数构造函数
public Ticket(uint distance)
{
this.distance = distance;
price = GetPrice();
}
//成员方法
//获得价格
private float GetPrice()
{
//0~100公里 不打折
//101~200公里 打9.5折
//201~300公里 打9折
//300公里以上 打8折
if ( distance > 300 )
{
return distance * 0.8f;
}
else if( distance >= 201 && distance <= 300 )
{
return distance * 0.9f;
}
else if (distance >= 101 && distance <= 200)
{
return distance * 0.95f;
}
else
{
return distance;
}
}
//展示信息
public void ShowInfo()
{
float perKMprice = price / distance;
Console.WriteLine("{0}公里{1}块钱", distance, price);
Console.WriteLine("平均每公里价格是"+perKMprice+"块钱");
}
}
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("构造函数析构函数练习题");
//主函数内
#region 练习题一
Person p1 = new Person("林文韬", 177.5f, 18, "成都");
Person p2 = new Person("小火", 184.5f);
//Person p3 = new Person();//写了有参构造函数 默认无参构造函数被顶掉 会报错
#endregion
#region 练习题二
Class c1 = new Class("Unity", 9999999);
Class c2 = new Class("C#", 9999999, new Student[] { new Student(), new Student() });
#endregion
#region 练习题三
Ticket t = new Ticket(1000);
t.ShowInfo();
//1000公里800块钱
//平均每公里价格是0.8块钱
#endregion
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com