Если перегрузка функций отсутствует, имя функции служит адресом кода функции, а когда функция вызывается, ее адрес легко найти, используя его имя. Однако с перегрузкой функции, как точно программа может найти правильный адрес функции? Есть ли скрытая таблица, похожая на виртуальные таблицы, где хранятся перегруженные функции с их адресом? Большое спасибо!
В С++ как обычно выполняется перегрузка функций?
Ответ 1
Все это было сделано во время компиляции. Компилятор С++ фактически изменяет имена функций, которые вы им даете внутренне, так что функция типа
int foo(int a, float b, char c)
внутренне получает имя, эквивалентное
func_foo_int_float_char()
(реальный символ обычно является некоторым gobbledygook как [email protected]@@[email protected]
).
Как вы можете видеть, имя оформляется в зависимости от точного количества и типов переданных параметров. Поэтому, когда вы вызываете функцию, компилятор легко просматривает параметры, которые вы передаете, украшайте им имя функции и придумывайте правильный символ. Например,
int a, b; float f; char c;
foo(a,f,c) ; // compiler looks for an internal symbol called func_foo_int_float_char
foo(a,b,c) ; // compiler looks for a symbol called func_foo_int_int_char
Опять же, все это сделано полностью во время компиляции.
Ответ 2
Компилятор может посмотреть на вызов и сопоставить его с известными существующими перегруженными реализациями и выбрать правильный. Нет необходимости в динамической таблице, все это прекрасно выполнимо статически во время компиляции.
Обновление: удалило мою попытку проиллюстрировать концепцию, показывая разные функции, которые может выбрать компилятор.
Ответ 3
Если вы говорите о перегруженных методах того же класса, например:
void function(int n);
void function(char *s);
...
objectInstance->function("Hello World")
Это время компиляции. Компилятор знает (или в некоторых ситуациях, делает лучшее предположение) в этой точке, какой метод вызывать.
Комментарий, который я сделал в вопросе, повторяю здесь.
Люди, которые рекомендуют манипулировать именами, ошибочны, я думаю. Это не похоже на то, что компилятор управляет именем и просто выполняет поиск среди искалеченных имен. Он должен вывести правильные типы из доступных методов. Как только он это сделает, он уже знает, какой метод вызывать. Затем он использует искомое имя в качестве последнего шага. Указание имени не является обязательным условием для определения перегруженной функции для вызова.
Ответ 4
Перегруженные функции разрешаются во время компиляции. Компилятор находит подходящее соответствие для данного набора параметров и просто вызывает соответствующую функцию по его адресу (void foo(int)
и void foo()
- практически две полностью независимые функции - если у вас есть foo(4)
в вашем коде, компилятор знает, какие функция для вызова).
Ответ 5
Это, я считаю, достигается с помощью обработки имени:
функции, которые вы знаете как foo (int) и foo (double), на самом деле называются как int_foo() и double_foo() (или аналогичные, я не совсем уверен в конкретной семантике, используемой для С++). Это означает, что символы С++ обычно на порядок больше, чем имена, которые они указаны в коде.
Ответ 6
Функциональная подпись состоит из имени функции + параметра (ов)
Ответ 7
Даже если нет перегрузки функций, компиляторы обычно управляют именами функций и переменных. Он называется name mangling. Это происходит как в C, так и в С++. Имя функции может быть украшено заметным (1) соглашением о вызове, (2) перегрузкой функции С++, (3) функцией члена класса.
GNU binutil c++filt
может декомпилировать это искаженное имя, а в Windows есть UnDecorateSymbolName
Ответ 8
Компиляторы С++ используют имя mangling (другое имя для каждой перегрузки), чтобы различать функции в объектном файле. Например
int test(int a){}
int test(float a,float b){}
int test(double a){}
int testbam(double a){}
создаст имена символов __Z4testi
, __Z4testff
, __Z4testd
, __Z7testbamd
. Это изменение имени сильно зависит от компилятора (к сожалению) и одна из многих причин, почему часто C предпочтительнее, чем С++.
При вызове функции test
компилятор сопоставляет типы аргументов и количество аргументов от каждой функции перегрузки. Затем используются прототипы функций, чтобы выяснить, какой из них следует вызывать.