c++析构函数三分法则

转载 2018年04月17日 19:00:49

对于新手来说,析构函数常常漏掉,导致内存泄漏

这里写的还不够完整,请多参考相关书籍


设计一个类时,如何写析构函数? 
析构函数如果我们不写的话,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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

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

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

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

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

int main(){
    NoName a;

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5

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

这里写图片描述

运行输出:

构造函数被调用了!
析构函数被调用了!
  • 1
  • 2

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

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

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

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行输出:

构造函数被调用了!
构造函数被调用了!
析构函数被调用了!
  • 1
  • 2
  • 3

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

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

    delete p;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行输出:

构造函数被调用了!
构造函数被调用了!
析构函数被调用了!
析构函数被调用了!
  • 1
  • 2
  • 3
  • 4

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

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

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

赋值构造函数:

  • 1

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

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

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

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

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

    NoName& operator =(const NoName &rhs);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
NoName& NoName::operator=(const NoName &rhs){
    pstring = new std::string;
    *pstring = *(other.pstring);
    i = other.i;
    d = other.d;
    return *this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

只要你写了析构函数,就必须要写 赋值构造函数 和 赋值运算符,这就是著名的 三法则 (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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

C++——三分答案模板——UmBasketella

UmBasketella Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8286   A...
  • McDonnell_Douglas
  • McDonnell_Douglas
  • 2017-01-19 11:09:33
  • 386

三分查找算法实现

  • 2013年10月10日 09:10
  • 819B
  • 下载

查找算法之三分查找

一. 概念 原文:http://blog.csdn.net/beiyouyu/article/details/7875480   在二分查找的基础上,在右区间(或左区间)再进行一次二分,这样的查找算法...
  • u010064842
  • u010064842
  • 2013-04-28 19:20:20
  • 3377

C++三法则:如果需要析构函数,则一定需要拷贝构造函数和赋值操作符

C++三法则:如果需要析构函数,则一定需要拷贝构造函数和赋值操作符 如何理解这句话,首先,从“如果需要析构函数”这里我们知道,类中必然出现了指针类型的成员(否则不需要我们写析构函数,默认的析构函数就...
  • u010025211
  • u010025211
  • 2015-08-27 15:36:59
  • 629

算法 三分法

转自:http://blog.csdn.net/pi9nc/article/details/9666627我们都知道 二分查找 适用于单调函数中逼近求解某点的值。 如果遇到凸性或凹形函数时,可以用三...
  • Littlewhite520
  • Littlewhite520
  • 2017-04-12 16:37:52
  • 1801

c++构造函数和析构函数的区别?

构造函数: 什么是构造函数?通俗的讲,在类中,函数名和类名相同的函数称为构造函数。它的作用是在建立一个对象时,作某些初始化的工作(例如对数据赋予初值)。C++允许同名函数,也就允许在一个类中有多个构...
  • xinsong520
  • xinsong520
  • 2016-08-25 18:35:39
  • 752

C++何时需要自定义析构函数呢?

对象销毁时  如果我们自己没有写析构方法,编译器会帮我们写一个然后调用。 那么问题来了,既然我不写,编译器会帮我写,那我干嘛要写??? 有木有什么情况必须我自己写的???? 处理内存的时...
  • love9099
  • love9099
  • 2015-01-24 17:51:42
  • 1426

浅谈C++类(7)--析构函数

欢迎转载,但请标明作者 “九天雁翎”,当然,你给出这个帖子的链接更好。 不知不觉我都写了6讲了,的确这样讲出来的学习才能迫使我真的去调试每个书上出现的代码,去想些自己能讲出什么新的书上没有的东西,这才...
  • vagrxie
  • vagrxie
  • 2007-04-28 12:41:00
  • 12436

c++析构函数的调用

// ConsoleApplication2.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include using namespace std;...
  • qq_15267341
  • qq_15267341
  • 2018-01-07 21:23:08
  • 481

C++为什么需要构造函数和析构函数

为什么要有构造函数和析构函数 在我们过去学习C语言编程的时候,我们通常生成的变量都是放在栈区里(auto存储类型)。然而,真正处理实际问题的程序却常常将变量或数组生成在堆区里。 假设我们定义了...
  • u013565071
  • u013565071
  • 2017-10-17 22:04:49
  • 435
收藏助手
不良信息举报
您举报文章:c++析构函数三分法则
举报原因:
原因补充:

(最多只允许输入30个字)