компоновка памяти объекта C

#c #object #memory-layout

Вопрос:

Насколько я понимаю, все функции-члены будут созданы в отдельной памяти при определении класса и являются общими для всех объектов. И только переменные-члены создаются индивидуально для каждого объекта. Но как функция-член выполняется при вызове с использованием объекта?

Где будет храниться адрес для этих функций-членов?

 
class B{
    public:
    int a;
    void fun(){

    }
};

int main(){
    B b;
    std::cout<<sizeof(b)<<std::endl;
}
 

Если я выполню эту программу, я получу результат в виде 4(который предназначен только для переменной-члена). Но вызов b.fun() правильно вызывает свою функцию-член. Как он вызывает, не сохраняя свой адрес внутри объекта? Где хранятся адреса функций-членов?

Есть ли что-нибудь похожее на схему памяти класса, в которой будут храниться эти адреса?

Комментарии:

1. Ваша сегодняшняя еда в Google — «искажение символов c «.

2. @SamVarshavchik Я не думаю, что искажение даст самый прямой ответ.

3. Допустим , вы создаете функцию, не являющуюся членом void fun(B *b) {} , и делаете fun(b); это . Теперь вас не удивляет, что несвязанная функция не влияет на размер B ? Функции-члены работают аналогично, просто синтаксис их вызова отличается.

4. Детали различаются в разных реализациях, но обычно невиртуальная функция-член вызывается так же, как и любая другая функция (со скрытым this параметром). Обычно адрес вызываемой функции «сохраняется» в самой инструкции вызова в качестве непосредственного операнда. Для указателя функции в другом месте не требуется места; в частности, он не хранится в макете памяти объектов этого типа. Если вы хотите узнать о внутренних компонентах, укажите свой компилятор / архитектуру / ОС / ABI / модель кода / и т.д.

Ответ №1:

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

Это делается автоматически компилятором, поэтому (в псевдокоде) ваш вызов b.fun() может быть скомпилирован в

 B::Fun(amp;b);
 

Где B::Fun можно рассматривать как обычную функцию. Адрес этой функции не обязательно должен храниться в реальном объекте (все объекты этого класса будут использовать одну и ту же функцию), и, следовательно, размер класса не включает его.

Комментарии:

1. Несвязанное примечание: То, как именно this входит в метод, не очень четко определено стандартом C . То, что описано выше, является самым прямым способом сделать это и единственным способом, который я видел в дикой природе. Но this это может быть скрытый последний параметр или перенесенный в метод с помощью магии эльфов. Дело в том, что вам на самом деле все равно, как this это проявляется в методе, пока это так.

Ответ №2:

Есть ли что-нибудь похожее на схему памяти класса, в которой будут храниться эти адреса?

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

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

В обоих случаях this вызываемой функции передается скрытый указатель. Это то, что «соединяет» его с вашим объектом.