(二)析构函数

析构函数

· 不能在结构中定义析构函数。只能对类使用析构函数。

· 一个类只能有一个析构函数。

· 无法继承或重载析构函数。

· 无法调用析构函数。它们是被自动调用的。

· 析构函数既没有修饰符,也没有参数。

析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

以C++语言为例,析构函数名也应与类名相同,只是在函数名前面加一个波浪符~,例如~stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数,它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。

解构器

我们知道,‘解构器’被用来清除类的事例。当我们在C#中使用解构器是,我们必须记住以下几点:

一个类只能有一个解构器。
解构器不能被继承或重载。
解构器不能被调用。他们是自动被(编译器)调用的。
解构器不能带修饰或参数。
下面是类MyClass解构器的一个声明:

 

~ Class()
{
// Cleaning up code goes here
}

 

程序员不能控制解构器何时将被执行因为这是由垃圾收集器决定的。垃圾收集器检查不在被应用程序使用的对象。它认为这些条件是符合清楚的并且收回它们的内存。解构器也在程序退出时被调用。当解构器执行时其背后所发生的那一幕是解构器隐式调用对象基类的Object.Finalize方法。因此上述解构器代码被隐含转化成:

 

protected override void Finalize()
{
try
{
// Cleaning up .
}
finally
{
base.Finalize();
}
}

现在,让我们看一个解构器怎样被调用的例子。我们有三个类A,B和C 。B派生自A,C派生自B。每个类有它们自己的构造器和解构。在类App的main函数中,我们创建C的对象。

 

using System;
class A
{
public A()
{
Console.WriteLine("Creating A");
}
~A()
{
Console.WriteLine("Destroying A");
}
}

class B:A
{
public B()
{
Console.WriteLine("Creating B");
}
~B()
{
Console.WriteLine("Destroying B");
}

}
class C:B
{
public C()
{
Console.WriteLine("Creating C");
}

~C()
{
Console.WriteLine("Destroying C");
}
}
class App
{
public static void Main()
{
C c=new C();
Console.WriteLine("Object Created ");
Console.WriteLine("Press enter to Destroy it");
Console.ReadLine();
c=null;
//GC.Collect();
Console.Read();
}
}

正如我们预料的,基类的构造器将会被执行并且程序会等待用户按‘enter’。当这个发生,我们把类C的对象置为null.但解构器没有被执行..!!??正像我们所说的,程序员无法控制解构器何时被执行因为这是由垃圾搜集器决定的。但程序退出时解构器被调用了。你能通过重定向程序的o/p到文本文件来检查这个。我将它输出在这里。注意到基类的解构器被调用了,因为在背后base.Finalize()被调用了。

 

Creating A
Creating B
Creating C
Object Created
Press enter to Destroy it
Destroying C
Destroying B
Destroying A
 
所以,如果一旦你使用完对象你就想调用解构器,你该怎么做?有两个方法:

调用垃圾搜集器来清理。

实现IDisposable的Dispose方法。

调用垃圾搜集器

你能通过调用GC.Collect方法强制垃圾搜集器来清理内存,但在大多数情况下,这应该避免因为它会导致性能问题。在上面的程序中,在GC.Collect()处移除注释。编译并运行它。现在,你能看到解构器在控制台中被执行了。

 

实现IDisposable接口

 

IDisposable 接口包括仅有的一个公共方法,其声明为void Dispose()。我们能实现这个方法来关闭或释放非托管资源如实现了这个接口的类事例所控制的文件,流,和句柄等。这个方法被用做所有任务联合对象的资源释放。当实现了这个方法,对象必须寻求确保所有拥有的资源被继承结构中关联的资源也释放(不能把握,翻不出来)。

 

class MyClass:IDisposable
{
public void Dispose()
{
//implementation
}
}

当我们实现了IDisposable接口时,我们需要规则来确保Dispose被适当地调用。

联合使用解构器和IDisposable接口

Public class MyClass:IDisposable
{
private bool IsDisposed=false;
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
protected void Dispose(bool Diposing)
{
if(!IsDisposed)
{
if(Disposing)
{
//Clean Up managed resources
}
//Clean up unmanaged resources
}
IsDisposed=true;
}
~MyClass()
{
Dispose(false);
}
}

在这里重载了Dispose(bool)来做清理工作,并且所有的清理代码都仅写在这个方法中。这个方法被解构器和IDisposable.Dispose()两着调用。我们应该注意Dispose(bool)没有在任何地方被调用除了在IDisposable.Dispose()和解构器中。

当一个客户调用IDisposable.Dispose()时,客户特意地想要清理托管的和非托管资源,并且因此完成清理工作。有一件你必须注意的事情是我们在清理资源之后立即调用了GC.SupressFinalize(this)。这个方法通知垃圾搜集器不需要调用解构器,因为我们已经做了清理。

注意上面的例子,解构器使用参数false调用Dispose。这里,我们确信垃圾搜集器搜集了托管资源。我们仅仅做非托管资源的清理。

结论

尽管如此我们花费一些时间实现IDisposable接口,如果客户不能合适地调用它们会怎样?为此C#有一个酷的解决方案。‘using’代码块。它看起来像这样:

using (MyClass objCls =new MyClass())
{

}
 
当控制从using块通过成功运行到结束或者抛出异常退出时,MyClass的IDispose.Dispose()将会被执行。记住你例示的对象必须实现System.IDisposable接口。using语句定义了哪个对象将被清除的一个范围。

注:
构造函数与析构函数的区别:
构造函数和析构函数是在类体中说明的两种特殊的成员函数。
构造函数的功能是在创建对象时,使用给定的值来将对象初始化。
析构函数的功能是用来释放一个对象的。在对象删除前,用它来做一些清理工作,它与构造函数的功能正好相反。

转载于:https://blog.51cto.com/3941954/727883

C# 中构造函数与析构函数(二)相关推荐

  1. C++中构造函数和析构函数可以抛出异常吗?

    C++中构造函数和析构函数可以抛出异常吗? 一.  析构函数 参照<Effective C++>中条款08:别让异常逃离析构函数.  总结如下: 1. 不要在析构函数中抛出异常!虽然C++ ...

  2. php中的构造函数和析构函数,php 中构造函数和析构函数

    构造函数: 在对象实例化时被调用,一个类中只能有一个构造函数,在类中起初始化的作用. 析构函数: 在对象结束时被自动调用. 话不多说,用一段代码来说明两者的区别: //定义一个类 class Pers ...

  3. 类中构造函数、析构函数与赋值函数的重写

    类中构造函数.析构函数与赋值函数的重写 class String { public: String(const char *str = NULL); // 普通构造函数 String(const St ...

  4. C++中构造函数和析构函数的调用顺序

    情况一:类B继承类A 结论一:定义变量时:先调用父类的构造函数,再调用派生类的构造函数:变量销毁时:先调用派生类的析构函数,再调用父类的析构函数. #include <iostream> ...

  5. C++中构造函数和析构函数

    [注]致力于将知识讲明白!不懂请留言! 构造函数 定义 它是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中. 另外,一个类可以有 ...

  6. C++虚继承中构造函数和析构函数顺序问题以及原理

    多重继承的问题:多个类B,C,-继承同一个类A导致如果X继承了B,C,-那么在X中将还有多个A中成员的拷贝,如果想要访问A中的成员如果不加名字空间将会导致二义性,这种拷贝大多是没有实际意义的,为了避免 ...

  7. C++:39---继承中构造函数、析构函数的关系

    一.继承中构造函数的关系 如果父类没有构造函数,则子类初始化时不需要构造父类 如果父类有构造函数,则子类初始化自己的构造函数时,要先初始化父类的构造函数 基类的构造函数必须在派生类的构造函数初始化列表 ...

  8. C++中构造函数和析构函数常见面试题?

    构造函数和析构函数常见面试题? 构造函数和析构函数常见面试题? 1.永远不要在构造函数或析构函数中调用虚函数 2.为什么构造函数不能定义为虚函数 3.为什么析构函数可以定义为虚函数 4.构造函数的执行 ...

  9. iOS中构造函数与析构函数

    一.构造函数 在OC中凡是已init开头的函数我们都称之为构造函数,在声明构造函数的时候,不带参数的一般直接声明为"-(id)init",带参数的一般声明为"-(id)i ...

最新文章

  1. CTFshow 信息收集 web17
  2. mdp文件-Chapter4-MD.mdp
  3. java解析json转Map
  4. 手绘风格的数据可视化 (萌萌风)Sketchify,及其他可视化工具(商业风)
  5. 白鹭引擎egret wing编辑器提示报错 egret 命令不存在 乱码的情况解决方法
  6. 【深度学习】学习深度学习的最好方法
  7. 公共技术点之 Android 动画基础
  8. mysql添加开机自启_【实操篇】如何设置MySQL开机自启动
  9. linux制作U盘启动盘无法使用,完美:使用syslinux制作U盘启动盘
  10. 正确模仿别人的php项目,一个模仿oso的php论坛程序(之一)第1/2页
  11. 单细胞文章专列——细胞图谱
  12. Excel中的Countif和Countifs
  13. linux 光盘刻录命令,在Linux操作系统中使用命令进行光盘刻录
  14. html移动端注册流程,登录和注册移动端.html
  15. 开利宣布收购空调企业广东积微集团,包括志高暖通等多家子公司
  16. 新能源硫酸钠溶液除镍钴锰分离树脂技术
  17. python爬虫——使用代理和xpath爬取豆瓣读书
  18. ChatGPT一路狂飙,对于教培机构是危险还是机遇?
  19. 尝试一下GNU Guile
  20. 中科点击:大数据应用的六种商业模式

热门文章

  1. SpringMVC从Controller跳转到另一个Controller
  2. 什么是WebSocket,它与HTTP有何不同?
  3. 深入理解SpringBoot(2)
  4. Buffer(缓冲/字节容器)详解
  5. quartz监听器使用
  6. java数据结构之快速排序
  7. Linux篇 | 用户、组和权限(一)
  8. 随心所欲的DateTime显示格式
  9. Max OS X下关于JDK的那些事
  10. ecshop每个商品添加去淘宝购买链接