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

    C++ 析构函数

    我使用的继承开发环境: Visual Studio 2010


    设计一个类时,如何写析构函数?
    析构函数如果我们不写的话,C++ 会帮我们自动的合成一个,就是说:C++ 会自动的帮我们写一个析构函数。很多时候,自动生成的析构函数可以很好的工作,但是一些重要的事迹,就必须我们自己去写析构函数。
    析构函数和构造函数是一对。构造函数用于创建对象,而析构函数是用来撤销对象。简单的说:一个对象出生的时候,使用构造函数,死掉的时候,使用析构函数。

    下面我们来做一个例子,看看:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class NoName{
    public:
        NoName():pstring(new std::string), i(0), d(0){}
    private:
        std::string * pstring;
        int i;
        double d;
    };
    
    int main(){
    
        return 0;
    }

    像上面这个 NoName 类这样的设计,类里面有一个成员变量是指针(std::string *pstring) ,那么在构造函数里我们使用 new 创建了对象,并使用 pstring 来操作这个对象。那么在这个情况下,我们就必须设计一个析构函数。

    析构函数是这样编写的:(可以在类的里面声明,定义写在类的外面,)

    class NoName{
    public:
        NoName():pstring(new std::string)
            , i(0), d(0){
            cout << "构造函数被调用了!" << endl;
        }
        ~NoName();
    NoName::~NoName(){
        cout << "析构函数被调用了!" << endl;
    }

    析构函数是这样写的: ~NoName() ,它与构造函数唯一的区别就是,前面都加了一个 ~ 符号。 析构函数都是没有参数的,也就是说:析构函数永远只能写一个。
    按照 C++ 的要求,只要有 new 就要有相应的 delete 。这个 new 是在构造函数里 new 的,就是出生的时候。所以在死掉的时候,就是调用析构函数时,我们必须对指针进行 delete 操作。

    我们来在main() 函数中创建一个 NoName 实例对象来 测试一下:

    int main(){
        NoName a;
    
        return 0;
    }

    并且在 main() 函数的 } 这一行添加一个断点,如图所示:

    这里写图片描述

    运行输出:

    构造函数被调用了!
    析构函数被调用了!

    在定义 a 对象的时候,调用了 NoName 类的构造函数,在main() 函数执行完的时候,就是执行完 return 0; 这句话后,main() 函数的执行域结束,所以就要杀掉 a 对象,所以这个时候会调用 NoName 类的析构函数。

    那么,如果我们在 main() 函数中使用 new 关键字来创建一个 NoName 类的实例化对象,会出现什么样的运行效果呢?
    main() 函数中的代码如下:

    int main(){
        NoName a;
        NoName *p = new NoName;
    
        return 0;
    }

    运行输出:

    构造函数被调用了!
    构造函数被调用了!
    析构函数被调用了!

    这里使用 new 创建的对象,就必须要使用 delete 来释放它。 牢牢的记住:newdelete 是一对。正确的代码是下面这个样子的:

    int main(){
        NoName a;
        NoName *p = new NoName;
    
        delete p;
        return 0;
    }

    运行输出:

    构造函数被调用了!
    构造函数被调用了!
    析构函数被调用了!
    析构函数被调用了!

    构造函数 和 析构函数 各有各的用途,在构造函数中,我们来获取资源;在析构函数中,我们来释放资源。释放了之后,这些资源就会被回收,可以被重新利用。
    比如说,我们在构造函数里打开文件,在析构函数里关闭打开的文件。这是一个比较好的做法。
    在构造函数里,我们去连接数据库的连接,在析构函数里关闭数据库的连接。
    在构造函数里动态的分配内存,那么在析构函数里把动态分配的内存回收。

    构造函数 和 析构函数 之间的操作是向对应的。
    如果我们不写析构函数,C++ 会帮我们写一个析构函数。C++帮我们写的这个析构函数只能做一些很简单的工作,它不会帮助我们去打开文件、连接数据库、分配内存这些操作,相应的回收,它也不会给我们写。所以需要我们自己手动的写。(如果要做这些操作,我们必须自己写。)

    如果我们自己写了析构函数,记住三个原则:
    如果你写了析构函数,就必须同时写赋值构造函数 和 赋值操作符。你不可能只写一个。

    赋值构造函数:

    在赋值的时候,不是讲指针赋值过来,而是将指针对应指向的字符串赋值过来,这是最关键的一步。

    因为我们写了析构函数,就必须要将赋值构造函数写上:

    class NoName{
    public:
        NoName():pstring(new std::string)
            , i(0), d(0){
            cout << "构造函数被调用了!" << endl;
        }
        NoName(const NoName & other);
        ~NoName();
    NoName::NoName(const NoName & other){
        pstring = new std::string;
        *pstring = *(other.pstring);
        i = other.i;
        d = other.d;
    }

    除了要写 赋值构造函数,还要写赋值操作符。

    class NoName{
    public:
        NoName():pstring(new std::string)
            , i(0), d(0){
            cout << "构造函数被调用了!" << endl;
        }
        NoName(const NoName & other);
        ~NoName();
    
        NoName& operator =(const NoName &rhs);
    NoName& NoName::operator=(const NoName &rhs){
        pstring = new std::string;
        *pstring = *(other.pstring);
        i = other.i;
        d = other.d;
        return *this;
    }

    只要你写了析构函数,就必须要写 赋值构造函数 和 赋值运算符,这就是著名的 三法则 (rule of three



    总结:

    在设计一个类的时候,如果我们一个构造函数都没有写,那么 C++ 会帮我们写一个构造函数。只要我们写了一个构造函数,那么 C++ 就不会再帮我们写构造函数了。

    构造函数可以重载,可以写很多个,析构函数不能重载,只能写一个。如果我们没有写析构函数,C++会自动帮我们写一个析构函数。那么在工作的时候,我们写的析构函数会被调用,调用完成之后,C++会执行它自动生成的析构函数。

    如果我们写的类是一个没有那么复杂的类,我们可以不需要写析构函数。如果一个类只要有这些情况:打开文件、动态分配内存、连接数据库。简单的说:就是只要构造函数里面有了 new 这个关键词,我们就需要自己手动编写析构函数。

    那么如果我们写了析构函数,就必须要注意三法则:同时编写:析构函数、赋值构造函数、赋值运算符。

    完整的代码:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class NoName{
    public:
        NoName():pstring(new std::string)
            , i(0), d(0){
            cout << "构造函数被调用了!" << endl;
        }
        NoName(const NoName & other);
        ~NoName();
    
        NoName& operator =(const NoName &rhs);
    private:
        std::string * pstring;
        int i;
        double d;
    };
    
    NoName::~NoName(){
        cout << "析构函数被调用了!" << endl;
    }
    
    NoName::NoName(const NoName & other){
        pstring = new std::string;
        *pstring = *(other.pstring);
        i = other.i;
        d = other.d;
    }
    
    NoName& NoName::operator=(const NoName &rhs){
        pstring = new std::string;
        *pstring = *(rhs.pstring);
        i = rhs.i;
        d = rhs.d;
        return *this;
    }
    
    int main(){
        NoName a;
        NoName *p = new NoName;
    
        delete p;
        return 0;
    }
  • 相关阅读:
    笔记:2016-06-20
    笔记2016-06-22
    javascript中的call(),apply(),bind()方法的区别
    JavaScript实现大整数减法
    Javascript实现大整数加法
    Javascript toString()、toLocaleString()、valueOf()三个方法的区别
    排序算法时间空间复杂度比较
    H-ui框架制作选项卡
    es6 解构赋值
    fullPage全屏高度自适应
  • 原文地址:https://www.cnblogs.com/aobosir/p/5928533.html
Copyright © 2011-2022 走看看