C ++编译器如何创建对象?

时间:2009-10-23 18:30:21

标签: c++ compiler-construction constructor

在C代码中如下:

{
   int i = 5;
   /* ....... */
}

编译器将通过向下移动堆栈指针(对于堆栈向下)移动int的大小来替换代码,并将值5放在该内存位置。

同样,在C ++代码中,如果创建了一个对象,编译器会做什么?例如:

class b
{
   public :
           int p;
           virtual void fun();
};

main()
{
   b   obj;
}

编译器会为上面的代码做些什么?任何人都可以解释何时分配内存,何时分配虚拟表的内存,以及何时调用默认构造函数?

6 个答案:

答案 0 :(得分:6)

On Constructions

逻辑上两者之间没有区别:

在这两种情况下,堆栈都足够大以容纳对象,并在对象上调用构造函数。

请注意:

  • POD类型的构造函数不执行任何操作。
  • 没有构造函数的用户定义类型具有编译器生成的默认cosntructor。

你可以这样想:

int   x;  // stack frame increased by sizeof(int) default construct (do nothing)
B     a;  // stack frame increased by sizeof(B)   default construct.

虽然:

int   y(6);  // stack frame increased by sizeof(int) Copy constructor called
B     b(a);  // stack frame increased by sizeof(B)   Copy constructor called

确定。当然,POD类型的构造函数非常简单,编译器会进行大量的优化(并且可能除了删除任何实际的代码甚至内存地址),但从逻辑上讲,将它想象成这样的方式就好了。 / p>

注意:所有类型都有一个复制构造函数(如果不这样,编译器会定义一个)和POD类型,你可以将它逻辑地认为是复制构造而没有任何问题。

至于虚拟表:

首先请注意,这是一个实现细节,并非所有编译器都使用它们 但是vtable本身通常是在编译时生成的。任何需要vtable的对象都有一个不可见的指针添加到结构中(这是作为对象大小的一部分包含的)。然后在构造期间,指针被设置为指向vtable。

注意:定义何时设置vtable是不可能的,因为标准没有定义,因此每个编译器可以随时自由地执行。如果你有一个多级层次结构,那么vtable可能是由每个构造函数从基数到大多数派生的,因此在最终构造函数完成之前可能是错误的。

注意:您无法在构造函数/析构函数中调用虚函数。所以你可以说只有在构造函数完全完成后才能正确初始化vtable。

答案 1 :(得分:3)

它在语义上是相同的,堆栈指针通过sizeof b递减(对于堆栈增长),然后调用默认构造函数来设置实例。

实际上,根据您的体系结构和编译器(以及传递给它的标志),除非确实需要,否则在int示例中的基本类型可能无法在堆栈上分配实际内存空间。它们将存在于寄存器中,直到需要实际存储器地址的操作(如&运算符)。

答案 2 :(得分:2)

触及有关何时分配虚拟表的问题。通常它在编译时完成(尽管它取决于编译器)。

虚拟表对于任何给定的类都是静态的。因此,编译器可以在编译时发出表。在类的初始化期间,指向虚拟表的指针被设置为存储的表。

因为同一个类的不同实例将指向同一个虚拟表。

答案 3 :(得分:1)

   b   obj;

与int一样,堆栈指针增加b的大小。然后调用构造函数。构造函数可以调用或不调用新函数或任何其他函数来分配内存。这是b的实施。呼叫本身不会启动任何分配。

vftable是一个静态对象。它不是用对象本身创建的。该对象包含一个指向其匹配的vftable的“不可见”指针。它的大小包括sizeof(b)。

答案 4 :(得分:0)

只需添加以前的答案,一旦构造了对象,编译器就会根据它的约定做任何必要的魔术,以保证在对象超出范围时调用析构函数。语言保证了这一点,但大多数编译器必须做一些事情来完成保证(比如设置一个指向析构函数的指针表和关于何时调用各种对象的各种析构函数的规则)。

答案 5 :(得分:0)

根据Itanium C++ ABI标准(例如,GCC遵循),虚拟表存储在一个单独的内存中,全局到翻译单元。

对于每个动态类,构造一个虚拟表,并以特定名称存储在目标文件中,如_ZTV5Class。运行时类型正好为Class的类将包含此表的指针。这些指针将被初始化,调整和访问,但没有类在其实例中包含其虚拟表。

所以答案是在编译时分配虚拟表,并且在构造期间只设置指向它们的指针。