如何通过示例使用 C++ 类构造函数和析构函数

举报
Tiamo_T 发表于 2021/09/29 13:32:52 2021/09/29
【摘要】 构造函数和析构函数是 C++ 类概念的基础。构造函数和析构函数或多或少都类似于普通函数(但有一些差异),它们被提供来增强类的能力。构造函数,顾名思义用于分配内存(如果需要)和构造类的对象,而析构函数用于在类对象被销毁时进行所需的清理。在本文中,我们将通过工作示例来研究构造函数和析构函数的概念。构造函数和析构函数让我们通过这个例子来理解构造函数和析构函数的概念:#include<iostrea...

构造函数和析构函数是 C++ 类概念的基础。构造函数和析构函数或多或少都类似于普通函数(但有一些差异),它们被提供来增强类的能力。

构造函数,顾名思义用于分配内存(如果需要)和构造类的对象,而析构函数用于在类对象被销毁时进行所需的清理。在本文中,我们将通过工作示例来研究构造函数和析构函数的概念。

构造函数和析构函数

让我们通过这个例子来理解构造函数和析构函数的概念:

#include<iostream>

class country
{
    public:

    country()
    {
        std::cout<<"\n Constructor called \n";
    }

    void setNumOfCities(int num);
    int getNumOfCities(void);

    ~country()
    {
        std::cout<<"\n Destructor called \n";
    }

    private:

    int num_of_cities;

};

void country::setNumOfCities(int num)
{
    num_of_cities = num;
}

int country::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
    country obj;
    int num = 5;

    obj.setNumOfCities(num);
    num = obj.getNumOfCities();

    std::cout<<"\n Number of cities is equal to "<<num;

    return 0;
}

在上面的例子中:

  • 类的名称是country
  • 观察到有两个函数与类的名称相同,即国家。
  • 名称前有'~'的函数是析构函数,而另一个是构造函数。

正如我们已经讨论过的,构造函数用于创建对象。准确地说,构造函数是一个特殊的函数,在创建类的对象时会自动调用它。类似地,析构函数是一个特殊的函数,当类对象被删除或超出范围时,它会被自动调用。

让我们通过执行上面显示的代码来验证上面给出的解释。
以下是在 Linux 上编译 C++ 代码的方式:

$ g++ -Wall cnstrDestr.cpp -o cnstrDestr
$

所以我们看到我们使用 g++ 编译器来编译 c++ 代码,在我们的例子中,编译的代码没有任何警告或错误。现在让我们执行代码:


$ ./cnstrDestr 

 Constructor called 

 Number of cities is equal to 5
 Destructor called

观察到当类的对象被创建时,类的构造函数被执行,并且就在对象即将被销毁时,析构函数被调用。所以这证实了这些特殊函数是在内部调用还是自动调用(从开发人员的角度来看)。

现在有人会问这些功能的实际作用是什么?在哪些场景中真正需要它们?

好吧,为了回答这个问题,让我们假设在同一个程序(如上所示)中,对 setNumOfCities() 和 getNumOfCities() 的调用是互换的。这意味着,现在代码尝试在设置之前获取该值。

这是更新的代码:

#include<iostream>

class country
{
    public:

    country()
    {
        std::cout<<"\n Constructor called \n";
    }

    void setNumOfCities(int num);
    int getNumOfCities(void);

    ~country()
    {
        std::cout<<"\n Destructor called \n";
    }

    private:

    int num_of_cities;

};

void country::setNumOfCities(int num)
{
    num_of_cities = num;
}

int country::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
    country obj;
    int num = 5;

 num = obj.getNumOfCities();
 obj.setNumOfCities(num);

    std::cout<<"\n Number of cities is equal to "<<num;

    return 0;
}

执行此代码时,输​​出如下:

$ ./cnstrDestr 

 Constructor called 

 Number of cities is equal to 134514633
 Destructor called

观察输出中产生了一些垃圾值。这是因为变量 'num_of_cities' 的值甚至在给它分配某个值之前就已获取。现在,这个问题的可能解决方案是什么?可以考虑使用类本身中的一些默认值来初始化变量。

就像是 :

#include<iostream>

class country
{
    public:

    country()
    {
        std::cout<<"\n Constructor called \n";
    }

    void setNumOfCities(int num);
    int getNumOfCities(void);

    ~country()
    {
        std::cout<<"\n Destructor called \n";
    }

    private:

 int num_of_cities = 0;

};

void country::setNumOfCities(int num)
{
    num_of_cities = num;
}

int country::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
    country obj;
    int num = 5;

    num = obj.getNumOfCities();
    obj.setNumOfCities(num);

    std::cout<<"\n Number of cities is equal to "<<num;

    return 0;
}

嗯,我们可以这样做吗?让我们编译这段代码并验证:

$ g++ -Wall cnstrDestr.cpp -o cnstrDestr
cnstrDestr.cpp:23:25: error: ISO C++ forbids initialization of member ‘num_of_cities’ [-fpermissive]
cnstrDestr.cpp:23:25: error: making ‘num_of_cities’ static [-fpermissive]
cnstrDestr.cpp:23:25: error: ISO C++ forbids in-class initialization of non-const static member ‘num_of_cities’

好吧,编译器抛出错误,抱怨这不能完成,因为这个变量是非静态的。所以,这不是正确的做事方式。那么如何用默认值初始化变量呢?是的,你猜对了,通过构造函数。由于构造函数也是类的成员函数,因此它们可以访问类私有数据成员。

这是如何做到的:

#include<iostream>

class country
{
    public:

    country()
    {
 num_of_cities = 0;
        std::cout<<"\n Constructor called \n";
    }

    void setNumOfCities(int num);
    int getNumOfCities(void);

    ~country()
    {
        std::cout<<"\n Destructor called \n";
    }

    private:

    int num_of_cities;

};

void country::setNumOfCities(int num)
{
    num_of_cities = num;
}

int country::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
    country obj;
    int num = 5;

    num = obj.getNumOfCities();
    obj.setNumOfCities(num);

    std::cout<<"\n Number of cities is equal to "<<num;

    return 0;
}

现在让我们编译并执行上面的代码:

$ g++ -Wall cnstrDestr.cpp -o cnstrDestr

$ ./cnstrDestr 

 Constructor called 

 Number of cities is equal to 0
 Destructor called

观察编译成功并产生了预期的输出。所以,这应该让你对构造函数和析构函数的力量有一个很好的了解。

在实际场景中,构造函数用于初始化类的数据成员,最重要的是为指针分配内存,析构函数用于清理此内存。

这是一个例子:

#include<iostream>

class country
{
    public:

    country()
    {
 num_of_cities = new(int);
        std::cout<<"\n Constructor called \n";
    }

    void setNumOfCities(int num);
    int getNumOfCities(void);

    ~country()
    { if(num_of_cities) delete num_of_cities;
        std::cout<<"\n Destructor called \n";
    }

    private:

 int *num_of_cities;

};

void country::setNumOfCities(int num)
{
    *num_of_cities = num;
}

int country::getNumOfCities(void)
{
    return (*num_of_cities);
}

int main(void)
{
    country obj;
    int num = 5;

    obj.setNumOfCities(num);
    num = obj.getNumOfCities();

    std::cout<<"\n Number of cities is equal to "<<num;

    return 0;
}

以下是有关构造函数和析构函数的一些要点:

  • 每当创建和销毁类对象(或超出范围)时都会调用它们。
  • 构造函数和析构函数通常在范围内保持公开。
  • 构造函数和析构函数都与类同名,并且没有返回类型。这意味着它们不能像任何其他普通函数一样返回值。
  • 如果没有明确提供构造函数或析构函数,编译器会在内部生成一个。
  • 默认构造函数,如果在类中显式声明,则是不接受参数或具有默认值参数的构造函数。
  • 构造函数和析构函数不能被继承。
  • 构造函数可以重载。
  • 析构函数不能接受参数。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。