代码之家  ›  专栏  ›  技术社区  ›  Mashmagar

编译器在哪里存储C++类的方法?

  •  5
  • Mashmagar  · 技术社区  · 16 年前

    这更像是一种好奇心…

    假设我有一个C++类Kitty如下:

    class Kitty
    {
        void Meow()
        {
            //Do stuff
        }
    }
    

    编译器是否将meow()的代码放在kitty的每个实例中?

    显然,到处重复相同的代码需要更多的内存。但另一方面,与在现代处理器上转移到内存中的绝对位置相比,转移到附近内存中的相对位置需要的汇编指令更少,因此这可能更快。

    我想这是一个实现细节,所以不同的编译器可能会执行不同的操作。

    请记住,这里我不考虑静态或虚拟方法。

    6 回复  |  直到 16 年前
        1
  •  3
  •   Julien Lebosquain    16 年前

    我认为,例如,方法的标准方法是像任何静态方法一样实现,只实现一次,但具有 this 指针传递到特定寄存器或堆栈上以执行调用。

        2
  •  4
  •   Jerry Coffin    16 年前

    在通常的实现中,任何给定函数只有一个副本。通过传递一个隐藏的参数(称为 this 在函数中)这是指向对象实例(及其数据)的指针。

    对于虚拟函数,事情会变得更加复杂:每个类都有一个vtable,其中包含一组指向虚拟函数的指针,并且每个对象都有一个指向其类的vtable的指针。通过查找vtable指针、查看正确的偏移量以及调用该指针指向的函数来调用虚拟函数。

        3
  •  2
  •   shoosh    16 年前

    不,不是这样的。
    不是的方法 virtual 与任何其他函数完全相同,但带有 this 指针。

    方法是 事实上的 invoked using a v-table . v-table是存储在对象数据旁边的函数指针列表。从某种意义上说,这更接近于您所描述的,但对于对象的所有实例,函数体始终是相同的。
    如果你有一个 static 方法中的变量。对于从不同实例调用的方法,静态变量将是相同的。

        4
  •  2
  •   R Samuel Klatchko    16 年前

    因为你对 Meow 在类定义中, 是隐式内联的。

    inline是编译器用函数的实际内容替换调用的提示。但这只是一个提示-编译器可以选择忽略该提示。

    如果编译器遵守提示,则每个调用都将替换为函数内容。这意味着编译器每次都会生成代码 而不是生成函数调用。

    如果编译器忽略了提示,编译器/链接器将安排所有调用都指向一个单一版本(因为它是内联的,经典策略是使用函数的每个翻译单元将获得一个单独的副本,其中包含指向链接器的指令,以仅保留一个版本)。

    最后,让我们来解释一下函数不是内联的地方。在这种情况下,编码人员需要确保定义恰好出现在一个翻译单元中,并且所有调用都将发送到此版本。

        5
  •  1
  •   Troubadour    16 年前

    不,编译器只为 Meow 一次一次 Kitty 实例使用的条件是成员是在行外编译的。如果编译器能够并且选择内联函数,那么它将在每个使用点(而不是与 凯蒂 )

        6
  •  0
  •   foobarfuzzbizz    16 年前

    编译器为自己的数据结构中的每个类(而不是对象)创建一个条目。类的此项包含指向该类方法的指针。

    对象在内存中表示为指向父类的指针及其实例字段的集合(因为每个对象的实例字段都不同)。然后,当调用一个方法时,该对象将跟随指向其父级的指针,然后跟随指向相应方法的指针。对象的指针也提供给方法,该方法充当此指针。

    虚拟方法有点复杂,但它们是以类似的方式完成的。

    如果你想了解更多,看看你是否可以参加编程语言课程。

    以下是对ASCII艺术的拙劣解释:

     obj                        class
    +------------+            +----------+
    | ptrToClass |----------->| method1  | ----------> toSomewhere(ptrToObj)
    |------------|            |----------|
    | field1     |            | method2  | ----------> toSomewhereElse(ptrToObj)
    +------------+            +----------+