当我没有声明constructor
为例时,编译器将为我提供一个default constructor
,它将没有参数且没有定义(正文),因此,将采用没有行动。
如果我现在不宣布destructor
,编译器将为我提供一个没有定义的default destructor
(正文),因此,我认为没有行动
所以,如果我完成了一个对象,那么对象使用的default destructor
重新分配(免费)内存是不是?如果没有,为什么我们得到它?
而且,同样的问题可能适用于default constructor
。如果它什么都没有,为什么它默认为我们创建?
感谢。
答案 0 :(得分:65)
说编译器生成的默认构造函数不采取任何操作是错误的。它等同于用户定义的构造函数,其中包含一个空主体和一个空的初始化列表,但这并不意味着它不采取任何操作。这是它的作用:
只有当一个类不是多态的,没有基类并且没有需要构造的成员时,编译器生成的默认构造函数才会执行任何操作。但即便如此,由于其他答案中解释的原因,有时也需要默认构造函数。
析构函数也是如此 - 它调用基类的析构函数和具有它们的所有成员的析构函数,因此在一般情况下,编译器生成的析构函数不会执行任何操作。
但内存分配确实与此无关。在调用构造函数之前分配内存,并且只有在最后一个析构函数完成后才释放它。
答案 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没有默认值 构造函数。编译器会 隐式定义默认值 构造函数
如果没有构造函数 为类明确定义。这个 隐式声明的默认值 构造函数相当于默认值 用空白体定义的构造函数。 (注意:如果有一些构造函数 定义,但它们都是非默认的, 编译器不会隐式 定义默认构造函数。这个 意味着默认构造函数可以 一个班级不存在。)