zoukankan      html  css  js  c++  java
  • C++之类的析构函数

     一、认识析构函数    

         在我的前一篇博客介绍了类的构造函数。http://www.cnblogs.com/MrListening/p/5557114.html。

         这里我们来简单说说类的析构函数,它是类的一个成员函数,名字由波浪号加类名构成。看它的名字我们大概就能联想到他功能,是执行与构造函数相反的操作:释放对象使用的资源,并销毁非static成员。

       同样的,我们来看看析构函数的几个特点:

    1.函数名是在类名前加上~,无参数且无返回值。

    2.一个类只能有且有一个析构函数,如果没有显式的定义,系统会生成一个缺省的析构函数(合成析构函数)。

    3.析构函数不能重载。每有一次构造函数的调用就会有一次析构函数的调用。

          

    拿程序说话:

    //by Mr_Listening,06 08 2016  
    class Date
    {
    public:
        Date(int year=1990,int month=1,int day=1)
            : _month(year), _year(month), _day(day)
        { }
        ~Date()
        {
            cout << "~Date()" << this << endl;
        }
    private:
        int _year=1990;   
        int _month;
        int _day;
    };
    void test()
    {
        Date d1;
    }
    int main()
    {
        test();
        return 0;
    }

          在test()函数中构造了对象d1,那么在出test()作用域d1应该被销毁,此时将调用析构函数,下面是程序的输出。当然在构建对象时是先调用构造函数的,在这里就不加以说明了。

               

          我们知道,在构造函数中,成员的在初始化是在函数体执行前完成的,并按照成员在类中出现的顺序进行初始化,而在析构函数中,首先执行函数体,然后再销毁成员,并且成员按照初始化的逆序进行销毁。

    二、销毁,清理?

          我们一直在说析构函数的作用是在你的类对象离开作用域后释放对象使用的资源,并销毁成员。那么到底这里所说的销毁到底是什么?那么继续往下看:

      void test ()

       {

           int a=10;

           int b=20;

       }

          回想我们在一个函数体内定义一个变量的情况,在test函数中定义了a和b两个变量,那么在出这个函数之后,a和b就会被销毁(栈上的操作)。那么如果是是一个指向动态开辟的一块空间的指针,我们都知道需要自己进行free,否则会造成内存泄漏。

          说到这里,其实在类里面的情况和这是一样的,这就是合成析构函数体为空的原因,函数并不需要做什么,当类对象出作用域时系统会释放你的内置类型的那些成员。但是像上面说的一样,如果,我的成员里有一个指针变量并且指向了一块你动态开辟的内存,那么像以前那样也需要自己来释放,此时就需要在析构函数内部写你的释放代码,这样在调用析构函数的时候就可以把你所有的资源进行释放。(其实这才是析构函数有用的地方,对吗)

    那么还有一点,当类类型对象的成员还有一个类类型对象,那么在析构函数里也会调用这个对象的析构函数。

    三、析构函数来阻止该类型对象被销毁?

           我们如果不想要析构函数来对对象进行释放该怎么做呢,不显式的定义显然是不行的,因为编译器会生成默认的合成析构函数。之前我们知道了如果想让系统默认生成自己的构造函数可以利用default,那么其实还有一个东西叫做delete。

        

    class Date
    {
    public:
    	Date(int year=1990,int month=1,int day=1)
    		: _year(year),_month(month),  _day(day)
    	{ }
    	~Date() = delete;
    	
    private:
    	int _year=1990;   
    	int _month;
    	int _day;
    };
    

         如果我这么写了,又在底下创建Date类型的对象,那么这个对象将是无法被销毁的,其实编译器并不允许这么做,直接会给我们报错。

       

            但其实是允许我们动态创建这个类类型对象的,像这样:Date* p = new Date;虽然这样是可行的,但当你delete p的时候依然会出错,原因就不用说了吧

    所以既然这样做的话既不能定义一个对象也不能释放动态分配的对象,所以还是不要这么用为好喽。

     四、注意喽

            一般在你显式的定义了析构函数的情况下,应该也把拷贝构造函数和赋值操作显式的定义。为什么呢??

           看下面的改动:

    class Date
    {
    public:
    	Date(int year=1990,int month=1,int day=1)
    		: _year(year),_month(month),  _day(day)
    	{ 
    		p = new int;
    	}
    	~Date()
    	{
    		delete p;
    	}
    	
    private:
    	int _year=1990;   
    	int _month;
    	int _day;
    	int *p;
    };
    

      成员中有动态开辟的指针成员,在析构函数中对它进行了delete,如果不显式的定义拷贝构造函数,当你这样:Date d2(d1)来创建d2,我们都知道默认的拷贝构造函数是浅拷贝,那么这么做的结果就会是d2的成员p和d1的p是指向同一块空间的,呢么调用析构函数的时候回导致用一块空间被释放两次,程序会崩溃的哦!

         

    最后,

        我们知道析构函数是可以调用的,那么构造函数可不可以呢?是怎样的使用环境

  • 相关阅读:
    mysql 8 查询报错(sql_mode=only_full_group_by)
    docker安装mysql8版本后 客户端连接出现client does not support authentication...
    docker常用命令
    查看tomcat日志相关Linux命令
    java项目部署到linux服务器涉及的命令
    ehcache与redis对比
    JS中调用BigDecimal处理金额
    thymeleaf模板 th:href 踩坑
    汇总一些绝对有价值的解决方案,边学习边收集
    spring注解总结,spring注解大全
  • 原文地址:https://www.cnblogs.com/MrListening/p/5567762.html
Copyright © 2011-2022 走看看