Как в 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

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

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

    
ответ дан 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
Показать остальные комментарии
11

Искажение имен .

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

int foo(int a, float b, char c) 

внутренне получает имя, эквивалентное

func_foo_int_float_char()

(реальным символом обычно является какая-то болтовня вроде [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
6

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

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

objectInstance->function("Hello World")  

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

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

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

    
ответ дан 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

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

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

    
ответ дан minjang 09.02.2010 в 08:59
-1

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

    
ответ дан AndiDog 09.02.2010 в 09:32