[英]How are constructors and destructors implemented in C++?
我有2个基本类和派生类(从Base公开派生)。 当我写-
Derived * d1 = new Derived;
delete d1;
编译器看到d1是派生类型对象。 因此,它将调用派生类构造函数(后者将调用基类构造函数)。 我在这里有2个问题-
1)为什么我们要遵循这个顺序?
2)这些构造函数如何一起工作以分配内存? 我需要一些实施细节
现在,下一个语句是delete d1。 因此,编译器认为d1是派生类型对象,因此调用了派生类的析构函数(删除了派生类成员后调用了基类的析构函数)。 我在这里有一个问题-
1)这些析构函数如何协同工作? 可以说派生类的析构函数在内存中传递了d1的地址。 这些析构函数如何释放空间?
该代码将无法编译。 您不能delete
自动实例,必须使用new
来分配实例,以便在其上使用delete
。
说到构造函数顺序,我认为运行它们很有意义,这样,更专业的类可以依赖于已经完成的更通用的部分。
编译器通过检查完整的类声明并通过任何超类和所有超类递归,可以知道每个类的内存布局。
1)默认情况下,基类构造函数会自动调用。 但是,允许派生的构造函数在其初始化程序列表中显式调用基类构造函数。 如果没有执行构造函数派生类优先,那将是不可能的。
Derived::Derived() : foo( 42 ), bar( 7 ), Base( 1, 2 ) { }
2)编译器知道派生类的内存占用(因为所有成员变量在编译时都是已知的),并分配足够的内存以容纳基类和派生类成员。 内存布局的详细信息由系统使用的ABI指定。
3)析构函数的作用不只是释放内存。 实际上,析构函数不会释放保存任何成员变量所需的内存-仅在必要时释放成员指针指向的内存。
您提供的代码示例无法编译。 delete
适用于指针,而不适用于对象。
我不认为C ++本身(语言标准)关于内存如何用于表示使用继承的类型实例的任何说法。 但是,在大多数实现中,从免费存储中分配了一块内存,该内存的大小足以容纳Base
和Derived
所有数据。 删除Derived
的实例时,两个构造函数都将运行,然后释放单个内存块。
类似地,如果Derived
的实例是某个类Container
的嵌入式数据成员,那么容纳Container
的实例的内存块将变得足够大,可以容纳Derived
(因此也就是Base
)以及Container
所有其他数据。
(1)基类不依赖于派生类,但是反之亦然。 也就是说, Base
类无法知道任何Derived
类具有哪些字段,因此Base::Base
不会也无法接触它们。 另一种可能的方式是, Derived::Derived
可以访问Base::member
。 因此,在Derived::Derived gets the chance to use
之前由Base::Base
初始化Base::member
Derived::Derived gets the chance to use
Base :: member`。
(2)构造函数不分配内存。 那是new
的任务。 或者,如果对象是全局对象,则为编译器的对象。 调用构造函数时, this
已经指向分配的内存; 它只需要填写该位置的成员即可。
在使用基本构造函数和派生构造函数的情况下,一个常见的实现在对派生构造函数的生成代码中插入对基本构造函数的调用。 通过单继承,Base和Derived类通常共享相同的this
指针,因此Derived构造函数可以传递它获得的相同指针。
(1) [sic]就像构造函数不分配内存一样,析构函数也不释放内存。 那是delete
的任务-同样,对于全局变量,编译器会执行delete
操作。
构造时,从最高的基类调用构造函数,直到最高级的基类中的一个称为最新的构造器。
调用析构函数时,顺序相反。 首先被称为派生最多的分解器,直到被称为最高基类的析构器。
在继承时,编译器应该知道它在继承什么。 它应该知道Base
类的组成。 它不能从没有任何想法的类继承。因此,先调用Base
类构造函数然后再调用Derived
确实有意义。
在Destruction的情况下,如果首先调用Base
析构函数并对其进行了Derived
,则Derived
类可能仍在使用Base类成员,这将变得无效。因此,Destruction是该构造的确切逆函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.