C ++默认析构函数

时间:2011-01-29 13:56:07

标签: c++ constructor destructor default-constructor

当我没有声明constructor为例时,编译器将为我提供一个default constructor,它将没有参数且没有定义(正文),因此,将采用没有行动

如果我现在不宣布destructor,编译器将为我提供一个没有定义的default destructor(正文),因此,我认为没有行动

所以,如果我完成了一个对象,那么对象使用的default destructor重新分配(免费)内存是不是?如果没有,为什么我们得到它?

而且,同样的问题可能适用于default constructor。如果它什么都没有,为什么它默认为我们创建?

感谢。

7 个答案:

答案 0 :(得分:65)

说编译器生成的默认构造函数不采取任何操作是错误的。它等同于用户定义的构造函数,其中包含一个空主体和一个空的初始化列表,但这并不意味着它不采取任何操作。这是它的作用:

  1. 它调用基类的默认构造函数。
  2. 如果类是多态的,则初始化vtable指针。
  3. 它调用拥有它们的所有成员的默认构造函数。如果有一个成员有一些构造函数但没有默认构件,那么这是一个编译时错误。
  4. 只有当一个类不是多态的,没有基类并且没有需要构造的成员时,编译器生成的默认构造函数才会执行任何操作。但即便如此,由于其他答案中解释的原因,有时也需要默认构造函数。

    析构函数也是如此 - 它调用基类的析构函数和具有它们的所有成员的析构函数,因此在一般情况下,编译器生成的析构函数不会执行任何操作。

    但内存分配确实与此无关。在调用构造函数之前分配内存,并且只有在最后一个析构函数完成后才释放它。

答案 1 :(得分:5)

因为如果您没有任何(可公开访问的)构造函数或析构函数,则无法实例化该类的对象。考虑:

class A
{
private:
    A() {}
    ~A() {}
};

A a;  // Oh dear!  Compilation error

如果没有显式声明任何构造函数或析构函数,编译器必须提供一个允许创建对象。

答案 2 :(得分:4)

默认析构函数不会做任何事情(就像默认构造函数一样)。

如果你的析构函数确实需要做某些事情(例如:释放一些资源),你需要自己定义一个。

请注意,通常您应该遵循rule of three:如果您的程序需要在其析构函数中执行某些操作(例如:释放资源),您还应该提供复制构造函数和赋值运算符; C ++还提供了它们的默认版本(再次,它们不会做任何事情)。

当您处理不需要执行任何操作的简单类时,默认构造函数/析构函数/赋值运算符/复制构造函数非常有用。一个特例是POD:它们(在C ++ 0x之前)甚至不能有显式的构造函数或析构函数。

答案 3 :(得分:4)

默认构造函数和析构函数只是一种商品,以防您不需要对您的类进行任何特殊操作,您不需要手动编写空版本。这对于其他OO语言是通用的,例如在Java中,如果成员的零初始化就足够了,则不需要提供构造函数。同时,它是向后兼容C的要求。如果C中有struct,它将没有构造函数或析构函数(C没有这些概念),以便能够处理该代码C ++必须是有效的代码。

另一种选择是在语言中声明一个类可能没有构造函数或析构函数,但是整个语言规范必须处理这样一个事实,即某些类型可能有构造函数和析构函数,而其他类型则没有,这将使语言更复杂,更难指定。虽然具有隐式定义的版本不会改变行为并简化规范。

在这个级别,就像术语 overrider 被应用于基类中的方法一样。在基类中,它不会覆盖任何东西,没有什么可以覆盖!然而,该语言明确指出在基础中声明的虚拟非纯方法是重写者。这使规范简单地说当通过指针或引用调用方法时将调用最终覆盖,而不必添加extre *或基本方法实现,如果该特定的覆盖器不存在该特定层次结构中的方法。

答案 4 :(得分:1)

简短的回答是,在C ++中,每个对象都需要构造函数和析构函数,即使它们没有做任何事情。因此,在后台为您创建它们的编译器满足了这一要求。

更长的答案是构造函数负责初始化类的成员。默认构造函数执行所有成员的默认初始化。 (这对POD类型没有任何意义,但是其他类会调用它们的默认构造函数。)

答案 5 :(得分:1)

使用智能指针时,默认的析构函数(请参阅Sergey的回答)对于避免内存泄漏至关重要。这是一个例子:

#include <iostream>
#include <memory>

using namespace std;

class Foo {
public:
  Foo(int n = 0): n(n) { cout << "Foo(" << n << ")" << endl; }
  ~Foo() { cout << "~Foo(" << n << ")" << endl; }
private:
  int n;
};

// notes:
// * default destructor of Bar calls destructors of unique_ptr<Foo> foo
//  and of unique_ptr<Foo[]> foo3, which, in turn, delete the Foo objects
// * foo2's Foo object leaks
class Bar {
public:
  Bar(): foo(new Foo(1)), foo2(new Foo(2)), foo3(new Foo[2]) { }
private:
  unique_ptr<Foo> foo;
  Foo* foo2;
  unique_ptr<Foo[]> foo3;
};

int main() {
  Bar bar;
  cout << "in main()" << endl;
}

此处为输出,显示只有foo2发生泄漏:

Foo(1)
Foo(2)
Foo(0)
Foo(0)
in main()
~Foo(0)
~Foo(0)
~Foo(1)

答案 6 :(得分:0)

默认的析构函数无法知道您的类“拥有”哪些内存以便能够释放它。

关于默认的构造函数部分,我将引用Wikipedia article这一个...

  

在C ++中,默认构造函数很重要,因为它们是   在某些情况下自动调用   情况:

     
      
  • 当声明一个没有参数列表的对象值时,例如我的课   X;;或动态分配否   参数列表,例如新的MyClass;该   默认构造函数用于   初始化对象
  •   
  • 当声明一个对象数组时,例如MyClass x [10] ;;要么   动态分配,例如新   MyClass [10];默认构造函数   用于初始化所有元素
  •   
  • 当派生类构造函数未显式调用基类时   初始化程序中的类构造函数   list,默认构造函数   基类叫做
  •   
  • 当类构造函数没有显式调用构造函数时   它的一个对象值字段   初始化列表,默认值   该字段的构造函数是   称为
  •   
  • 在标准库中,某些容器使用“填充”值   值为的默认构造函数   未明确给出,例如   载体(10);初始化   矢量与10个元素,是   填充默认构造   我们的类型的价值。
  •   
     

在上面   如果是这样的话,那是一个错误   class没有默认值   构造函数。编译器会   隐式定义默认值   构造函数

     

如果没有构造函数   为类明确定义。这个   隐式声明的默认值   构造函数相当于默认值   用空白体定义的构造函数。   (注意:如果有一些构造函数   定义,但它们都是非默认的,   编译器不会隐式   定义默认构造函数。这个   意味着默认构造函数可以   一个班级不存在。)