Как компилятор C реализует обратный порядок вызовов деструкторов при наследовании?

#c

#c

Вопрос:

Например, мы знаем, что следующий код будет выполняться как подробно:

 #include <iostream>

class Base {
public:
  virtual ~Base() {
    std::cout << "Destroying base" << std::endl;
  }
};

class Derived: public Base {
public:
  ~Derived() {
    std::cout << "Destroying derived" << std::endl;
  }
};

int main()
{
  Base* p = new Derived();
  delete p;
}

  
 $ g   --std=c  11 main.cc -o main; ./main
Destroying derived
Destroying base
  

Как это поведение реализуется компилятором? В ответе, вероятно, также потребуется рассказать о реализации vtable.

Высокоуровневый, простой для понимания ответ высоко ценится!

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

1. Что вы имеете в виду под «как это реализовано»? Стандарт C определяет алгоритм, и компилятор просто следует ему.

2. Я не понимаю, какой ответ вы ищете. Существует спецификация C , и компилятор может реализовать ее так, как он хочет.

3. повторно — спасибо за ответ. Я не очень хорошо знаком со спецификацией C , и под «реализацией» я подразумеваю на уровне обсуждения как статического вывода, так и того, как динамически работает генерируемый код.

4. @Yksisarvinen это может быть, но они не вынуждены это делать, поэтому вы не можете быть уверены, если не проверите код каждого отдельного компилятора, и даже тогда он может измениться в любое время только для одного компилятора (маловероятно, но все же теоретически возможно). Я не настолько хорошо разбираюсь в реализациях компилятора, поэтому я бы не знал, как ответить на вопрос о конкретном, но ответ на более общий «как компиляторы это реализуют?» — это старое доброе «Это зависит».

5. Если вас действительно интересуют подробности, вот генерация кода Clang для деструктора класса

Ответ №1:

С точки зрения C конструкторы и деструкторы являются специальными функциями. Например, у них нет возвращаемых типов.

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

В этом конкретном случае компилятор, скорее всего, встроит вызов Base::~Base в двоичный Derived::~Derived файл . Это достаточно просто, нет виртуального или множественного наследования, усложняющего ситуацию.

Комментарии предполагают, что может быть задействована vtable. Очевидно, что для компиляторов, использующих vtables, им нужно будет поместить запись for Derived::~Derived в эту таблицу, поскольку она virtual . Вероятно, это будет та же обычная функция, которая содержит встроенный Base::~Base вызов.