Если нет никакой перегрузки функции, имя функции служит адресом функционального кода, и когда функция вызывается, ее адрес легок найти использование ее имени. Однако с перегрузкой функции, как точно программа может найти корректный функциональный адрес? Существует ли скрытая таблица, подобная виртуальным таблицам, который снабжает перегруженные функции их адресом? Большое спасибо!
Компилятор может просмотреть вызов и сопоставить его с известными существующими перегруженными реализациями и выбрать правильную. Нет необходимости в динамической таблице, все это прекрасно выполняется статически во время компиляции.
Обновление: удалила мою попытку проиллюстрировать концепцию, показывая функции с разными именами, между которыми компилятор может выбирать.
Все сделано во время компиляции. На самом деле компилятор C++ модифицирует внутри себя имена функций, которые вы даете, так что функция типа
int foo(int a, float b, char c)
внутри получает имя, эквивалентное
func_foo_int_float_char()
(реальным символом обычно является какой-нибудь gobbledygook типа ?CFoo@Foo@@QAAX_N@Z
).
Как видите, название украшается в зависимости от точного количества и типов передаваемых параметров. Таким образом, при вызове функции компилятору легко посмотреть на передаваемые параметры, украсить ими имя функции и придумать правильный символ. Например,
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
Опять же, все это делается полностью во время компиляции.
Если вы говорите о перегруженных методах одного и того же класса, например:
void function(int n);
void function(char *s);
...
objectInstance->function("Hello World")
Это штука времени компиляции. Компилятор знает (а в некоторых ситуациях лучше всего угадывает), какой метод вызывать.
Комментарий, который я сделал в вопросе, повторяю здесь.
Люди, которые предлагают искажение имени, думаю, ошибаются. Это не так, как если бы компилятор искажает имя и просто ищет среди искаженных имен. Он должен сделать вывод о правильных типах из доступных методов. Как только он это делает, он уже знает, какой метод вызывать. Затем он использует искаженное имя в качестве последнего шага . Искажение имени не является обязательным условием для определения, какую перегруженную функцию вызывать.
Перегруженные функции разрешаются во время компиляции. Компилятор находит подходящее соответствие для заданного набора параметров и просто вызывает соответствующую функцию по ее адресу ( void foo (int)
и void foo ()
практически две полностью независимые функции. - если в вашем коде есть foo (4)
, компилятор знает, какую функцию вызывать).
Я полагаю, это достигается путем изменения имени:
функции, которые вы знаете как foo (int) и foo (double), на самом деле называются как-то как int_foo () и double_foo () (или аналогичные, я не совсем уверен в конкретной семантике, используемой для C ++). Это означает, что символы C ++ обычно на порядок больше, чем имена, которые им даны в коде.
Даже при отсутствии перегрузки функций компиляторы обычно искажают имена функций и переменных. Это называется искажением имени . Это происходит как в C, так и в C ++. Имя функции может быть оформлено, в частности, (1) соглашением о вызовах, (2) перегрузкой функций C ++, (3) функцией-членом класса.
GNU binutil c ++ filter
может удалить это искаженное имя, а в Windows есть UnDecorateSymbolName
Сигнатура функции состоит из имени функции + тип(ы) параметра(ов)
.C ++ компиляторы используют изменение имен (разные имена для каждой перегрузки), чтобы различать функции в объектном файле. Например,
int test(int a){}
int test(float a,float b){}
int test(double a){}
int testbam(double a){}
создаст имена символов __ Z4testi
, __ Z4testff
, __ Z4testd
, __ Z7testbamd
. Это искажение имен сильно зависит от компилятора (к сожалению) и является одной из многих причин, почему часто C предпочтительнее C ++.
При вызове функции test
компилятор сопоставляет заданные типы аргументов и количество аргументов для каждой перегрузки функции. Затем прототипы функций используются для определения того, какой из них следует вызвать.