Где компилятор хранит методы для классов C++?

Это - больше любопытство, чем что-либо еще...

Предположим, что у меня есть класс C++ Kitty следующим образом:

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

Компилятор помещает код для Мяуканья () в каждом экземпляре Kitty?

Очевидно, повторение того же кода везде требует большей памяти. Но с другой стороны, ветвление к относительному местоположению в соседней памяти требует меньшего количества инструкций по сборке, чем ветвление к абсолютному местоположению в памяти на современных процессорах, таким образом, это потенциально быстрее.

Я предполагаю, что это - деталь реализации, таким образом, различные компиляторы могут работать по-другому.

Следует иметь в виду, я не рассматриваю статические или виртуальные методы здесь.

5
задан Mashmagar 25 May 2010 в 19:18
поделиться

6 ответов

Я считаю, что стандартный способ реализации методов экземпляра должен быть реализован как любой статический метод, только один раз, но с передачей указателя this в конкретный регистр или стек для выполнения вызова.

3
ответ дан 13 December 2019 в 05:31
поделиться

Компилятор создает запись для каждого класса (не объекта) внутри его собственной структуры данных. Эта запись для класса содержит указатели на методы этого класса.

Объект представлен в памяти как указатель на родительский класс и коллекцию полей его экземпляра (поскольку они различны для каждого объекта). Затем, когда вызывается метод, объект следует за указателем на своего родителя, который затем следует за указателем на соответствующий метод. Указатель на объект также передается методу, который действует как этот указатель.

Виртуальные методы немного сложнее, но они выполняются аналогичным образом.

Если вы хотите узнать больше, попробуйте посетить занятия по языкам программирования.

Вот плохая попытка объяснить это с помощью ASCII-арта:

 obj                        class
+------------+            +----------+
| ptrToClass |----------->| method1  | ----------> toSomewhere(ptrToObj)
|------------|            |----------|
| field1     |            | method2  | ----------> toSomewhereElse(ptrToObj)
+------------+            +----------+
0
ответ дан 13 December 2019 в 05:31
поделиться

Поскольку определение Meow находится внутри определения класса, Meow неявно является inline.

inline - это подсказка компилятору заменить вызов на фактическое содержимое функции. Но это только подсказка - компилятор может проигнорировать подсказку.

Если компилятор проигнорирует подсказку, то каждый вызов будет заменен содержимым функции. Это означает, что компилятор будет генерировать код каждый раз, когда вызывается Meow, вместо того, чтобы генерировать вызов функции.

Если компилятор игнорирует подсказку, компилятор/линкер организует единственную версию, к которой будут направляться все вызовы (поскольку функция встроенная, классическая стратегия заключается в том, что каждый блок трансляции, использующий функцию, получает отдельную копию с указаниями компоновщику хранить только одну версию).

Наконец, перейдем к объяснениям, когда функция не является встроенной. В этом случае от кодера требуется, чтобы определение появилось ровно в одной единице трансляции, и все вызовы будут направлены на эту единственную версию.

2
ответ дан 13 December 2019 в 05:31
поделиться

Нет, компилятор генерирует код для Meow только один раз и каждый Kitty Экземпляр использует, при условии, что член был скомпилирован вне очереди. Если компилятор может и выбирает встроить функцию, то она дублируется в каждой точке использования (а не с каждым экземпляром Kitty ).

1
ответ дан 13 December 2019 в 05:31
поделиться

В обычной реализации существует только одна копия любой данной функции. Связь между кодом и данными для данного экземпляра объекта устанавливается путем передачи скрытого параметра (называемого в функции this), который является указателем на экземпляр объекта (и его данные).

Для виртуальных функций все немного сложнее: каждый класс получает vtable, содержащую набор указателей на виртуальные функции, а каждый объект получает указатель на vtable для своего класса. Виртуальные функции вызываются путем нахождения указателя на vtable, поиска нужного смещения и вызова функции, на которую указывает этот указатель.

4
ответ дан 13 December 2019 в 05:31
поделиться

Нет, так не делается.
Методы, которые не являются виртуальными, точно такие же, как и любая другая функция, но с дополнительным аргументом для указателя this.

Методы, которые являются виртуальными, вызываются с помощью v-таблицы. v-таблица - это список указателей функций, которые хранятся рядом с данными объектов. В некотором смысле это ближе к тому, что вы описываете, но все же тело функции всегда одинаково для всех экземпляров объекта.
Это можно продемонстрировать, если в методе есть статическая переменная. Статическая переменная будет одинаковой для методов, вызываемых из разных экземпляров.

2
ответ дан 13 December 2019 в 05:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: