В C ++ как обычно выполняется перегрузка функций?

17

Если перегрузка функций отсутствует, имя функции служит адресом кода функции, а когда функция вызывается, ее адрес легко найти, используя его имя. Однако с перегрузкой функции, как точно программа может найти правильный адрес функции? Есть ли скрытая таблица, похожая на виртуальные таблицы, где хранятся перегруженные функции с их адресом? Большое спасибо!

    
задан Rick 09.02.2010 в 08:46
источник
  • Люди, которые рекомендуют манипулировать именем, ошибочны, я думаю. Это не похоже на то, что компилятор управляет именем и просто выполняет поиск среди искалеченных имен. Он должен вывести правильные типы из доступных методов. Как только он это сделает, он уже знает, какой метод вызывать. Затем он использует искомое имя в качестве последнего шага. Обработка имени не является обязательным условием для определения перегруженной функции для вызова. –  09.02.2010 в 09:34
  • Возможно, этот комментарий должен идти в одном из ошибочных ответов, на которые вы ссылаетесь? –  Manuel 09.02.2010 в 09:37
  • Их слишком много, поэтому я оставил его здесь. –  Feb 9 '10 at 8:39 09.02.2010 в 09:39
  • Совершенно верно. Учитывая void foo (int); и void foo (std :: string) ;, foo (1.0f) вызовет первый. В предложениях «name mangling» был бы поиск компилятора? foo (float) и сбой. –  MSalters 09.02.2010 в 10:19
  • Если вам нужен адрес перегруженной функции, иногда вам нужно передать этот адрес соответствующему типу. Предположим, что у вас есть две функции foo, одна из которых принимает int, а другая принимает float, и вы хотите, чтобы адрес принимающей int, вы пишете static_cast <void (*) (int)> (& foo). –  fredoverflow 09.02.2010 в 12:00

8 ответов

12

Название mangling .

Все это делается во время компиляции. Компилятор C ++ фактически изменяет имена функций, которые вы им даете внутренне, так что функция типа

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

Опять же, все это делается полностью во время компиляции.     

ответ дан Crashworks 09.02.2010 в 08:55
источник
  • Это наоборот. Компилятор сначала смотрит на все foos, выбирает тот, который соответствует всем правилам в стандарте, а затем, возможно, испускает искаженное имя для компоновщика. –  UncleBens 09.02.2010 в 15:22
  • Очень информативно, спасибо. –  Dasaru 25.06.2012 в 16:39
11

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

Обновление: удалило мою попытку проиллюстрировать концепцию, показывая разные функции, которые может выбрать компилятор.

    
ответ дан unwind 09.02.2010 в 08:48
источник
  • Люди говорят об этом техническом имени Mangling. Поиск по этому ключевому слову. –  SunnyShah 09.02.2010 в 09:05
  • Обозначение имени - это просто различие между именами идентификаторов, перегруженными или нет. Основная цель манипулирования именами заключается не в перегрузке, а во избежании конфликтов имен. В перегруженном случае компилятор должен определить, какой метод вызывать, и где находится основная логика. Я считаю, что OP хотел знать, было ли это время компиляции / времени выполнения. –  09.02.2010 в 09:24
  • Это неточно, обычно это компоновщик, который должен вычислить адрес вызова. Название украшения очень важно. Да, есть возможность опустить запрос ссылки, если функция находится в одной и той же единицы перевода. Действительно ли это происходит, это деталь реализации. –  Hans Passant 09.02.2010 в 10:25
  • @unwind: Итак, что произойдет, если я позвоню foo (1.0)? Компилятор ищет void fo_double (double z), который не существует. Как говорили другие, название mangling - это не ответ. –  jalf 09.02.2010 в 11:08
  • @nobugz: компилятор может определить, какой метод вызывает и генерирует правильное имя функции (будь то искалеченное или нет), которое затем использует компоновщик. Вы говорите то же самое, что я сказал. Переплетенные имена - всего лишь одно из решений, позволяющих избежать конфликтов во время связывания. Они не требуются для обеспечения перегрузки функций. Функция перегрузки функций может изменить способ искажения имен, но это всего лишь следствие наличия уникальных имен. –  Feb 9 '10 at 18:41 09.02.2010 в 19:41
Показать остальные комментарии
5

Если вы говорите о перегруженных методах того же класса, например:

void function(int n);
void function(char *s);
...

objectInstance->function("Hello World")  

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

Комментарий, который я сделал в вопросе, повторяю здесь.

Люди, которые рекомендуют манипулировать именами, ошибочны, я думаю. Это не похоже на то, что компилятор управляет именем и просто выполняет поиск среди искалеченных имен. Он должен вывести правильные типы из доступных методов. Как только он это сделает, он уже знает, какой метод вызывать. Затем он использует искомое имя как шаг last . Указание имени не является обязательным условием для определения перегруженной функции для вызова.

    
ответ дан Aryabhatta 09.02.2010 в 08:50
источник
  • Тому, кто дал -1. По крайней мере оставить комментарий. Я могу либо удалить / отредактировать сообщение, если информация неточна. Дайте мне шанс учиться! –  09.02.2010 в 08:59
  • Я предполагаю, что этот человек (а не я) был заблокирован, потому что вы действительно не дали четкого ответа. Вместо этого вы просто говорите «время компиляции» и «компилятор просто знает, что делать», что действительно немного неточно. –  AndiDog 09.02.2010 в 09:34
  • Ну, имя mangling неточно, IMO. Самое лучшее, что мы можем сказать, это «компилятор знает». Каждый компилятор может иметь свою собственную реализацию. –  09.02.2010 в 09:36
  • Название mangling помогает (насколько мне известно, требуется) компоновщика. –  ChrisW 09.02.2010 в 09:42
  • Вы также можете скопировать свой комментарий, чтобы расслабиться, так как вы публикуете другое использование названия, которое интересно. –  yeyeyerman 09.02.2010 в 10:13
3

Перегруженные функции разрешаются во время компиляции. Компилятор находит подходящее соответствие для заданного набора параметров и просто вызывает соответствующую функцию по его адресу ( void foo(int) и void foo() - практически две полностью независимые функции - если у вас foo(4) в вашем коде, компилятор знает, какие функция для вызова).

    
ответ дан Alexander Gessler 09.02.2010 в 08:49
источник
0

Это, я считаю, достигается с помощью обработки имени:

функции, которые вы знаете как foo (int) и foo (double), на самом деле называются как int_foo () и double_foo () (или аналогичные, я не совсем уверен в конкретной семантике, используемой для C ++). Это означает, что символы C ++ обычно на порядок больше, чем имена, которые они указаны в коде.

    
ответ дан Williham Totland 09.02.2010 в 08:49
источник
-1

Функциональная подпись состоит из имени функции + параметра (ов)

    
ответ дан bashmohandes 09.02.2010 в 08:54
источник
-1

Даже если нет перегрузки функций, компиляторы обычно управляют именами функций и переменных. Он называется имя mangling . Это происходит как в C, так и в C ++. Имя функции может быть украшено заметным (1) соглашением о вызове, (2) перегрузкой функции C ++, (3) функцией члена класса.

GNU binutil c++filt может декомпилировать это искаженное имя, а в Windows есть UnDecorateSymbolName

    
ответ дан minjang 09.02.2010 в 08:59
источник
-1

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

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 компилятор сопоставляет типы аргументов и количество аргументов от каждой перегрузки функции. Затем используются прототипы функций, чтобы выяснить, какой из них следует вызывать.

    
ответ дан AndiDog 09.02.2010 в 09:32
источник