2010-03-20 152 views
11

您好我有一个关于this指针的问题,当一个对象被构造时,它被初始化了吗?这意味着什么时候可以使用它?虚拟表是在构造函数中构造的,是否与this指针相同?什么时候在C++中初始化“this”指针?

例如,我有这样的代码。输出是8.这是否意味着在构造函数被输入之前,this指针已经被初始化了?

class A{ 
    public: 
    A() { cout<<sizeof(*this);} 
    int i; 
    int *p; 
}; 

int main() {  
    A a; 
} 

如果它是真的,那么在输入构造函数之前还会发生什么?

如果不是这样,this指针何时初始化?

+5

编译器知道* this的大小,它会生成一个硬编码的值。也许你需要一个更好的例子。 – 2010-03-20 15:57:34

+2

sizeof是编译时的东西(我不会说硬编码),并且不依赖于运行时“this”指针中包含的值。另外,vtable(应该有一个)不是“在构造函数中”构造的。 Neil: – 2010-03-20 16:02:33

+0

:真的吗?如果不是int构造函数,那是什么? – skydoor 2010-03-20 16:12:25

回答

13

this指针不是对象或类的成员 - 它是您调用的方法的隐式参数。因此,它的传递方式与其他参数非常相似 - 除非您不直接询问它。

在上例中,构造函数是一种特殊的方法,它又是一种特殊的函数。当你构造对象,编译器分配它的内存(在这种情况下,在栈上,如amain函数的局部变量。然后,它会自动调用构造函数初始化对象。

作为调用的一部分构造函数,隐参数this - 一个指向你的对象 - 传递作为参数

在具有以下签名的方法...

void MyMethod (const int* p) const; 

其实有两个参数,两个指针。 。有显式参数p和隐式参数this。该行末尾的const指定this是一个常量指针,与前面的指定p是一个常量指针一样。因为this是隐式传递的,所以只能存在对这种特殊语法的需求,所以不能像其他参数那样以常规方式指定常量。

“静态”方法没有隐含的“this”参数,并且无法直接访问对象成员 - 可能没有与该调用关联的特定对象。它基本上是一个标准函数,而不是一个方法,除了访问私有成员(提供它可以找到一个要访问的对象)。

正如Steve Fallows指出的那样,sizeof (this)在编译时是已知的,因为它是一个指针类型,所有指针(* 1)具有相同的sizeof值。你看到的“8”意味着你正在编译一个64位平台。this此时可用 - 它指向有效内存,并且所有成员都已完成其构造函数调用。但是,它不一定是完全初始化的 - 毕竟你仍然在构造函数中。

编辑

* 1 - 严格,这可能不是真的 - 但是编译器知道它在这里处理什么类型的指针与即使该值直到运行时才知道。

+0

+1,很好的解释。在调用约定的情况下:this这个:http://msdn.microsoft.com/en-us/library/984x0h58%28v=VS.71%29。aspx – 2010-03-20 17:28:20

2

sizeof(* this)在编译时已知。所以cout声明没有揭示这个初始化。

鉴于构造函数可以立即开始访问该对象的成员,显然这是在构造函数开始之前初始化的。

在构造函数之前发生了什么?很可能是任何事情。我不认为标准限制了编译器可以做什么。也许你应该指定你想要的任何事情。

7

this指针未被存储。当为占用特定内存位置的对象调用构造函数时,该位置将作为参数传递给构造函数和其他成员函数。

如果this会存储在对象内部,如何检索那个指针?对,您需要再次使用this指针:)

+0

只是为了在需要的情况下添加一些解释 - 当您访问方法内的对象成员时,编译器在后台使用'this'来查找对象和成员。 'member1'和'this-> member1'实际上是一样的东西 - 第一种形式只是简写。 – Steve314 2010-03-20 16:37:52

+0

+1为你的最后语句发送给我一个无限递归 – Dario 2010-03-20 16:40:10

0

该指针是每个类的调用(包括构造函数)的第一个参数。

当一个类方法被调用时,类的地址被最后压入堆栈(假设这里是cdecl调用约定)。这被读回寄存器以用作这个指针。

构造函数实际上被称为好像它们是普通的成员函数。

你不能有一个虚拟的构造函数,因为构造函数负责设置vtable成员。

+0

以上的一些或全部可能适用于你的特定编译器,但你所说的都不是C++标准指定的。 – 2010-03-20 16:09:51

2

虚拟表是在构造函数中构造的,与这个指针是一样的吗?

虚拟表不在构造函数中构造。
通常,同一个类的所有实例共享一个单一的全局v-表,每个单独的类都有它自己的全局v-表。
v表在编译时已知,在程序加载时“已构造”。

this指针“构造”(我认为“分配”是一个更好的词)在分配时,也就是global new operator调用后,并进入构造函数之前。

在对象被堆栈分配而不是堆分配的情况下,全局变量new未被调用,但this仍然可用,因为在输入构造函数之前分配了栈空间。

实例vptr在分配对象的内存之后以及构造函数被调用之前分配。

+2

vtable不是在那里构建的,但是指向vtable(vptr)的指针仍然需要填写。编辑 – kennytm 2010-03-20 16:09:04

+0

以提及分配之后和构建之前分配的vptr。 – abelenky 2010-03-20 16:12:28

+0

所有这一切都假定有一个vtable--一个编译器可以以任何想要的方式实现运行时分派,只要它达到预期的结果即可。这可能意味着在运行时每次调用都会修改某种数据结构(也许是为了让最常见的调用运行得更快) - 虽然我在这里是“在理论上”而不是“在现实中”,因为它很难打败效率的虚拟表。 – Steve314 2010-03-20 16:16:43

0

正如nobugz已经指出的那样,您的示例没有多大意义 - sizeof根据您传递给它的对象的类型得出结果。它在而不是在运行时评估其操作数。

这就是说,是的,this在进入ctor之前被初始化。基本上,编译器为对象分配空间(如果对象具有自动存储持续时间,则在堆栈上,或者如果它具有动态存储持续时间,则使用::operator new)。进入ctor后,基类的ctors(如果有的话)已经运行完毕。当您调用ctor时,this会给出为对象分配的内存地址。

0

这个开始指向当前对象,并且在输入构造函数体之前,所有成员和基类都已经初始化。

因此,你可以伸手指向在初始化列表,但接收方应该做的不是别的,只是将它存储等,因为所指向的情况下可能无法完全在当时建造。

#include <iostream> 

class B; 

class A 
{ 
    B* b_ptr; 
public: 
    A(B* b); 
}; 

class B 
{ 
    A a; 
    int i; 
public: 
    B(): a(this), i(10) {} 
    void foo() const { std::cout << "My value is " << i << '\n'; } 
}; 

A::A(B* b): 
    b_ptr(b) //Ok to store 
{ 
    b_ptr->foo(); //not OK to use, will access initialized member 
} 

int main() 
{ 
    B b; 
} 
2

这是否意味着在进入构造函数之前,该this指针已初始化?

,则this指针的值是称为构造甚至被称为前。此值可通过构造函数中的this关键字,构造函数initialization lists,析构函数,成员方法获得。 this关键字在表面上作为(指针类型的)方法变量行事,但不是一个;它通常位于寄存器(x86平台上的ecx),通常不能编译像&this这样的代码。

进入

的构造函数之前会发生些什么,至少至于this指针而言,出现这种情况(除非使用placement new)的第一件事是分配的内存最终由this指向,无论是在堆栈上(如你的例子)还是堆上(使用new)。此时this指针是已知的。然后,默认构造函数或明确指定的构造函数(通过构造函数初始化列表)然后在基类(如果有的话)和您的类非POD成员变量(如果有)上调用。类vtable指针也设置在此点之前,如果您的类包含虚拟方法或析构函数。然后,你的类构造函数体,如果有的话,被调用。 (构造函数是递归调用的,即当调用基类的构造函数时,调用后者的基类构造函数,然后调用非POD成员构造函数,并设置基类'vtable指针,接着是类的构造函数体。