≡菜单

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

构造函数和析构函数是C ++中类概念的基础。构造函数和析构函数都或多或少类似于提供用于增强类功能的常规函数​​(但有一些区别)。

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

如果您不熟悉C ++,则应首先了解C ++的概念。 C ++类 .

构造函数和析构函数

让’通过以下示例了解构造函数和析构函数的概念:

#include<iostream>

class  国家 
{
    public:

     国家 ()
    {
        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  国家 ::setNumOfCities(int num)
{
    num_of_cities = num;
}

int  国家 ::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
     国家  obj;
    int num = 5;

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

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

    return 0;
}

在上面的示例中:

  • 班级的名字是 国家 .
  • 请注意,有两个函数与类具有相同的名称,即国家/地区。
  • 具有一个功能‘~’名称之前是析构函数,而另一个是构造函数。

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

让’通过执行上面显示的代码,验证以上给出的解释。
这是在3d捕鱼达人上编译C ++代码的方式:

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

因此,我们看到我们使用g ++编译器来编译c ++代码,在本例中,该代码已编译而没有任何警告或错误。现在让’s execute the code :

$ ./cnstrDestr 

 Constructor called 

 Number of cities is equal to 5
 Destructor called

观察到在创建类的对象时,将执行该类的构造函数,并且在该对象即将销毁时,将调用析构函数。因此,这确认了这些特殊功能是在内部或自动(从开发人员’s point of view).

现在有人问’这些功能的实际作用是什么?在哪些情况下确实需要它们?

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

这是更新的代码:

#include<iostream>

class  国家 
{
    public:

     国家 ()
    {
        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  国家 ::setNumOfCities(int num)
{
    num_of_cities = num;
}

int  国家 ::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
     国家  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  国家 
{
    public:

     国家 ()
    {
        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  国家 ::setNumOfCities(int num)
{
    num_of_cities = num;
}

int  国家 ::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
     国家  obj;
    int num = 5;

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

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

    return 0;
}

Well, can we do this? 让’编译此代码并验证:

$ 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  国家 
{
    public:

     国家 ()
    {
 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  国家 ::setNumOfCities(int num)
{
    num_of_cities = num;
}

int  国家 ::getNumOfCities(void)
{
    return num_of_cities;
}

int main(void)
{
     国家  obj;
    int num = 5;

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

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

    return 0;
}

现在让’s编译并执行以上代码:

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

$ ./cnstrDestr 

 Constructor called 

 Number of cities is equal to 0
 Destructor called

观察到编译成功并且产生了预期的输出。因此,这应该使您对构造函数和析构函数的功能有了一个很好的了解。

在现实世界中,构造函数用于初始化类的数据成员,最重要的是将内存分配给 指针 和析构函数用于清除此内存。

这是一个例子:

#include<iostream>

class  国家 
{
    public:

     国家 ()
    {
 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  国家 ::setNumOfCities(int num)
{
    *num_of_cities = num;
}

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

int main(void)
{
     国家  obj;
    int num = 5;

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

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

    return 0;
}

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

  • 每当创建和销毁(或超出范围)类对象时,就会调用它们。
  • 构造函数和析构函数通常在范围上保持公开。
  • 构造函数和析构函数的名称与类的名称相同,并且没有返回类型。这意味着它们不能像其他任何普通函数一样返回值。
  • 如果未显式提供构造函数或析构函数,则编译器会在内部生成一个。
  • 如果在类中显式声明了默认构造函数,则该默认构造函数是不接受任何自变量或具有默认值的自变量的构造函数。
  • 构造函数和析构函数不能被继承。
  • 构造函数可以重载。
  • 析构函数不能接受参数。

注意,我们还没有介绍C ++中的继承概念。在讨论继承时,我们将详细讨论构造函数和析构函数的一些特征。

如果您喜欢这篇文章,您可能还会喜欢..

  1. 50个3d捕鱼达人 Sysadmin教程
  2. 50个最常用的3d捕鱼达人命令(包括示例)
  3. 排名前25位的最佳3d捕鱼达人性能监视和调试工具
  4. 妈妈,我找到了! – 15个实用的3d捕鱼达人 Find命令示例
  5. 3d捕鱼达人 101 Hacks第二版电子书 3d捕鱼达人 101黑客手册

Bash 101 Hacks书 Sed和Awk 101黑客手册 Nagios Core 3书 Vim 101黑客手册

{ 9 评论 … 加一 }

  • 鲍勃 ,上午11:59

    很棒的文章。谢谢!!!

  • 安东尼·贝恩斯 ,晚上7:15

    这太好了’我学习C ++,这会教给我更多的东西,我喜欢它。保持它来。

  • 菲利普·佩特林科 2013年4月7日,下午4:26

    感谢您的这篇文章,非常感谢您的工作。

    只是关于防御性编程的最佳实践的改进:

    变更:
    if(num_of_cities)

    至 :
    if(num_of_cities!= NULL)

    1)使用显式编码进行阅读和理解更合适
    2)显式测试可能会检测到错误,例如,如果您使用了错误的变量(该变量不是指针)
    3)Unix哲学指出,首要任务是构建一个按设计工作的程序,然后测量性能,并在需要时使用适当的优化。如果这样可以提供更好的性能,则可以编写简化的测试–但我怀疑这会不会有所帮助。

    — Philippe

  • Aridzonan_13 2013年4月23日,晚上8:54

    I’我对所有的例子都印象深刻’我读过。这是最好的之一。非常简洁,呈现得很好。

  • 尼尔冷却 2013年4月30日,上午2:36

    我通常喜欢这里的帖子,但是与此相关的确有很多问题。我想您可能想在以后的报道中谈谈,所以我对此表示歉意’是这样。我的问题是:
    (1)风格差–对于构造函数,最佳做法是始终使用“成员初始化列表” syntax, i.e.
    国家 ():num_of_cities(new(int))
    {

    在这种情况下(一个指针)’s不是必需的,但是在某些情况下是必需的,因此通常被认为是好的做法(请参阅Sutter的C ++编码标准中的规则48& Alexandrescu)
    (2)与new(int)相比,首选new int(),因为这将使动态内存初始化为零(适用于所有基本类型)
    (3)你的避风港’t涵盖了RAII的概念,即使用构造函数初始化内存,例如
    国家/地区(int num):num_of_cities(new int(num))
    {

    int main()
    {
    国家obj(5);
    (4)通过在指针中使用带有指针的示例并在析构函数中进行删除,您引入了潜在的内存泄漏,如下所示:
    国家obj(5);
    国家obj1(obj); //使用浅拷贝复制构造函数
    国家obj2(0);

    obj2 = obj; // op =浅拷贝
    此代码将引起有趣的行为(核心转储),因此您要么必须覆盖复制consructor和operator =的重载,要么更好的是,您应该按照2003年tr1中的介绍使用shared_ptr
    (5)对于C ++ 11(受g ++支持),允许初始化对象成员变量,例如
    int num_of_cities = 5;
    我希望你’以后再讲。

  • 黄昏科西嘉 2013年4月30日,上午7:11

    首先,函数和方法之间有区别,我不’不用考虑那些哲学家,但是有一个。
    函数没有针对初学者的指针….
    而且它们不是正常功能,存在差异,人们不应该’不要与那些事情混淆!!!

  • 黄昏科西嘉 2013年5月2日,上午1:30

    让我告诉这个
    我很多次’甚至没有编写构造函数或析构函数,但是一开始人们应该编写它们,直到得到为止。
    〜应该以不同的方式使用,现在点网已经有了所谓的垃圾回收。
    不好,我知道这是3d捕鱼达人的世界,但是最好是了解所有方法,然后再决定哪一个对您来说是最好的。

  • 凯文·吉道 2015年3月9日,上午11:35

    n…这篇文章帮助我…it good

  • 辛恩 2017年4月26日,上午2:31

    有用的

发表评论