编译器:类实例化代码是如何编译的

Compiler: How is class instantiation code compiled?

本文关键字:编译 何编译 实例化 代码 编译器      更新时间:2023-10-16

假设我们有(在C++中):MyClass* x = new MyClass(10)

有人能解释一下当编译器解析这个语句时"到底"发生了什么吗?(我试着看了一下《红龙》这本书,但找不到任何有用的东西)。

我想知道堆栈/堆或编译器的符号表中发生了什么。编译器如何跟踪x变量的类型?稍后对x->method1(1,2)的调用将在MyClass中解析为适当的方法(为了简单起见,假设没有继承,MyClass是我们唯一的类)。

MyClass* x是指向类型为MyClass的对象(实例)的指针的定义。该变量的内存是根据其定义的位置分配的:如果它在方法中定义,并且是局部变量,则使用堆栈。存储地址的是内存。

则表达式new MyClass(10)是为对象(实例)本身在堆中分配内存并返回要存储在x中的地址的命令。为了填充新对象MyClass的内存(设置其初始状态),会自动执行特殊方法(至少一个)-构造函数(在某些情况下是几个)-在您的示例中接收值10

因为C++允许继承(这也是创建实例时执行几个构造函数的原因),所以有一些机制可以确定应该准确调用哪个方法。您应该在某处阅读有关虚拟方法表的内容。

在最简单的情况下(没有继承),变量x的类型(指向MyClass类型对象的指针)提供了有关对象结构的所有必要信息。因此,x->method1(1,2)(*x).method1(1,2)提供对成员method1的调用,以使用参数12(存储在堆栈中)以及形成对象状态的数据(存储在堆中)来执行该调用,并且该数据可由类的任何非静态成员内的this指针使用。当然,方法本身并没有存储在堆中。

更新:

你可以举例做同样的实验,比如:

#include <iostream>
#include <string>
using namespace std;
class MyClass
{
private:
    int innerData;
    long long int lastValue;
public:
    MyClass() // default constructor
    {
        cout << "default constructor" << endl;
        innerData = 42;
        lastValue = 0;
    }
    MyClass(int data) // constructor with parameter
    {
        cout << "constructor with parameter" << endl;
        innerData = data;
        lastValue = 0;
    }
    int method1(int factor, int coefficient)
    {
        cout << "Address in this-pinter " << this << endl;
        cout << "Address of innerData " << &innerData << endl;
        cout << "Address of lastValue " << &lastValue << endl;
        cout << "Address of factor " << &factor << endl;
        lastValue = factor * innerData + coefficient;
        return lastValue;
    }
};
int main(void)
{
    MyClass* x = new MyClass(10);
    cout << "addres of object " << x << endl;
    cout << "addres of pointer " << &x << endl;
    cout << "size of object " << sizeof(MyClass) << endl;
    cout << "size of pointer " << sizeof(x) << endl;
    x->method1(1, 2);
}

C++确实有点令人讨厌,而且这已经在C中开始了。只需看看前3个标记:MyClass * x。编译器必须查找MyClass以确定这是而不是乘法。由于它是一个类型,所以它也不应该查找x,而是将x添加到符号表中(真的不能延迟)。在简单解析语言的理想世界中,不需要为解析的每个令牌保持符号表的最新状态。

在定义了x之后,=发出初始化表达式的信号。这很容易解析:new是一个明确的关键字,它不是一个放置new,并且正在创建的类型就在那里。