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