Почему виртуальный метод генерирует неопределенную ссылку на _sbrk?

Это изящно компилируется:

class dummy {
};

Это жалуется на неопределенную ссылку на _sbrk:

class dummy {
    virtual ~dummy();
};

Почему виртуальный метод генерирует неопределенную ссылку на _sbrk?

Раньше я думал, что vtable размещается где-то статически и не требует malloc.

Компилятор: arm-none-eabi-gcc 8.0.0 с последним newlib. Скомпилировано с помощью -fno-rtti -fno-exceptions -fno-unwind-tables.

Тестовая программа (boot похожа на main):

class base {
public:
  virtual ~base();
};

class dummy : public base {
public:
  ~dummy();
};

base::~base() {
  __BKPT();
}

dummy::~dummy() {
  __BKPT();
}

extern "C" void _sbrk() {
  __BKPT();
}

void boot() {
  for(;;) {
    base b;
    dummy d;
  }

  return 0;
}

person Piotr Jedyk    schedule 19.08.2017    source источник
comment
Покажите минимальную проверенную программу.   -  person Vlad from Moscow    schedule 19.08.2017
comment
Показать точную командную строку компилятора   -  person M.M    schedule 20.08.2017


Ответы (3)


Производные классы могут иметь собственный оператор удаления. Эта функция используется очень редко — по моему опыту, почти никогда. Виртуальный деструктор позволяет вызывать правильный оператор удаления при использовании выражения удаления (delete p).

Компилятор определенно сгенерировал виртуальный деструктор с удалением, который вызывает специфичный для класса оператор удаления, который почти во всех случаях оказывается глобальным оператором удаления (::operator delete), но его также можно переопределить локальным оператором, определенным в классе.

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

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

РЕДАКТИРОВАТЬ: стандартная цитата

Из [class.dtor]/12 :

В точке определения виртуального деструктора (включая неявное определение) функция освобождения памяти без массива определяется так же, как для выражения delete this, появляющегося в невиртуальном деструкторе. > класса деструктора (см. [expr.delete]). Если поиск терпит неудачу или если функция освобождения имеет удаленное определение, программа неправильно сформирована. [ Примечание. Это гарантирует, что функция освобождения, соответствующая динамическому типу объекта, доступна для выражения удаления ([class.free]). — конец примечания ]

"виртуальный деструктор"... определяется так, как если бы "не виртуальный деструктор" содержал выражение — это словоблудие, которое может быть трудно расшифровать: поскольку деструктор виртуальный, почему мы говорим о виртуальном деструкторе ? (Мне пришлось прочитать приведенный выше стандартный текст несколько раз.)

Другой способ просмотра - это с точки зрения возможной реализации (некоторые реализации использовали именно это):

Каждый деструктор на самом деле принимает логический параметр deallocate, а компилятор добавляет код:

if (deallocate)
  delete this;

непосредственно перед концом тела деструктора, и все деструкторы вызываются с аргументом false, кроме одного полного объекта, когда вызывается оператор delete.

person curiousguy    schedule 20.08.2017
comment
При добавлении void operator delete(void *) в класс он скомпилировался без зависимости _sbrk. Это действительно отличный ответ; подробно объяснил ;-) - person Piotr Jedyk; 20.08.2017
comment
Проголосовал за вас снова, потому что это качественный материал. Пожалуйста, придерживайтесь того, что вы знаете, или будьте достаточно скромны (и проверяйте факты), когда отваживаетесь в новые области. Просто используйте мозг, который дал вам Бог. Я пометил ваши другие комментарии к модам, поэтому они были удалены. - person Paul Sanders; 10.07.2018

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

загляните в свой файл карты и посмотрите, что вызывает _sbrk, что вызывает его и т. д., пока не найдете корень.

см. этот пост с дополнительной информацией: абстрактный класс (чистый виртуальный метод) существенно увеличивает размер двоичного файла

p.s. vtables не требуют динамического выделения памяти

person Russ Schultz    schedule 19.08.2017
comment
Нет чисто виртуальной функции. Кроме того, throw действительно не должен зависеть от распределения памяти! - person curiousguy; 20.08.2017

Раньше я думал, что vtable размещается где-то статически и не требует malloc.

Не знаю, откуда вы взяли это предположение:

  • Нет никаких гарантий относительно того, как реализуются виртуальные таблицы, и в стандартах не говорится, что динамическая диспетчеризация вообще должна быть реализована с использованием виртуальных таблиц. Это просто самый распространенный метод.
  • В вашем вопросе мне не хватает доказательств того, что _sbrk действительно втягивается, потому что деструктор virtual

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

person tofro    schedule 19.08.2017
comment
If you want to use virtual methods, you need to have dynamic memory allocation: используя вашу фразу, Не уверен, откуда вы взяли это предположение. Можете ли вы уточнить? Это противоречит моему собственному опыту, по крайней мере, на встроенных системах. - person Dan; 19.08.2017
comment
@Dan Виртуальные методы имеют смысл только с полиморфизмом, а полиморфизм требует доступа к методу через указатель базового класса - в противном случае (все статически выделенные) вы могли бы просто жить полностью без виртуальных функций с тем же эффектом. - person tofro; 19.08.2017
comment
Ничего о виртуальных функциях или полиморфизме не требует кучи, malloc или sbrk. - person Russ Schultz; 20.08.2017