C/C++教程

C++构造函数与析构函数

本文主要是介绍C++构造函数与析构函数,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

构造函数与析构函数

  • 前言
  • 0x00 为什么要有构造函数和析构函数?
  • 0x01 构造函数
  • 0x02 析构函数
  • 0x03 构造函数与析构函数的失败问题

前言

C++中的构造函数和析构函数,分别用于实例化对象的初始化工作以及对象资源的回收工作。


0x00 为什么要有构造函数和析构函数?

在一个类中,通常会有该类成员变量的接口以供外部访问。实例化一个类对象以后,要使用接口函数(Set函数)来设置类对象的值。

class Date
{
private:
    int nYear;
    int nMonth;
    int nDay;

    char* szFormat; // 格式化的日期字符串
public:
    void SetYear(int nYear);
    void SetMonth(int nMonth);
    void SetDay(int nDay);
    void SetDate(int nYear, int nMonth, int nDay);

    // get接口省略...

    char* GetDate(); // 得到格式化的日期
};

int main(int argc, char* argv[])
{
    Date date;
    date.SetDate(2022, 2, 27); // 使用接口函数来设置对象的值

    return 0;
}

这里有一个问题,就是实例化对象时,对象没有初始值。用接口来设置初值会感觉很不自然。并且格式化日期指针没有值。

如果按照C语言的想法,会提供一个Init函数用来实现对象的初始化。那么在实例化对象时,必须先调用Init方法用于初始化该对象。Init方法必须在实例化以后立即调用。

void Init(int nYear, int nMonth, int nDay)
{
   this->nYear = nYear;
   this->nMonth = nMonth;
   this->nDay = nDay;
   szFormat = (char*)maloloc(strlen("1970-01-01") + 1);
}

char* GetDate()
{
    // format过程省略
    return szFormat;
}

该对象还有个保存堆空间的指针变量,为此还需要为该对象提供一个释放对象资源方法Destroy函数。

void Destroy()
{
    if (szFormat != nullptr)
    {
        free(szFormat);
        szFormat = nullptr;
    }
}

因此,正常使用该类的步骤应该是:

Date date;
date.Init(2022, 2, 27);
// do something ...
data.Destroy();

现在的问题是,如果使用者忘了调用或者没有调用Init函数和Destroy函数则可能会有内存泄漏。放在其他项目中可能会有意想不到的结果。

为此C++提供了构造函数和析构函数,分别用于实例化对象的初始化以及对象资源的回收。能够完美解决上面的问题。

0x01 构造函数

在实例化对象时会自动调用构造函数。

1.与类名同名,无返回值。
2.允许重载。
3.不允许显式调用。因为如果构造函数中有申请内存,显式调用会重复申请内存,原来申请的内存地址将会被覆盖。例如上面的Init函数的重复调用。但vs中,可以使用[对象.类名::构造函数]来显式调用。

class VectorInt2D  //类
{
public:
    VectorInt2D()
    {
        Init();
    }
    VectorInt2D(int x) // 构造函数可以重载
    {
        Init(x);
    }
    VectorInt2D(int x, int y) // 构造函数可以重载
    {
        Init(x, y);
    }
    void Init(int x = 0, int y = 0)
    {
        uint32_t nLen = strlen("VectorInt2D") + 1;
        m_pszClassName = new char[nLen];
        strcpy_s(m_pszClassName, nLen, "VectorInt2D");

        this->x = x;
        this->y = y;
    }
    ~VectorInt2D()
    {
        if (m_pszClassName != nullptr)
        {
            delete m_pszClassName;
            m_pszClassName = nullptr;
        }
    }

    char* GetClassName()
    {
        return m_pszClassName;
    }

private:
    char* m_pszClassName;
    int  x;
    int  y;
};

int main(int argc, char* argv[])
{
    VectorInt2D pos1(2, 1);  //实例化对象自动调用构造
    pos1.VectorInt2D::VectorInt2D(); // 非标准

    //pos1.VectorInt2D(); //error 不允许显式调用

    return 0;
}

0x02 析构函数

在对象出作用域时调用。

1.函数名为类名前~,无返回值。
2.不允许带参数,不允许重载。
3.可以显示调用。向上面Destroy函数的调用,可以通过写代码来控制资源的合理释放,因此可以显式调用。

int main(int argc, char* argv[])
{
    VectorInt2D pos1(2, 1); 
    pos1.~VectorInt2D(); // 显示调用析构函数
    return 0;
}

0x03 构造函数与析构函数的失败问题

可以在构造函数中调用析构函数,因此构造函数中如果申请内存失败,可以直接调用析构函数。但是构造函数没有返回值,如何让外部知道构造函数是否成功?


这篇关于C++构造函数与析构函数的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!