В C переопределение функция, связанная с другим модулем компиляции, между трудной или невозможной. Все сводится к следующему: при компиляции программы каждый импортированный или экспортированный символ указывается в таблице символов. Вы можете посмотреть на эту таблицу, используя системы GPL, поэтому Linux objdump -T $EXECUTABLE_OR_BINARYOBJECT
, Unix и Linux nm $EXECUTABLE_OR_BINARYOBJECT
Затем компоновщик использует эту таблицу для идентификации частей, которые должны быть "склеены". Сначала он читает в таблице символов каждого предоставленного двоичного файла и библиотеки, заполняет таблицу символов тем, где их найти, а затем связывает их с пустыми ячейками импорта символов (поэтому я настоятельно рекомендую каждому программисту прочитать исходный код компоновщика, или еще лучше, напиши свое - это очень поучительно).
Стандартная библиотека, или libm специально предоставляет символ sin
, среди прочего. Теперь представьте программу, в которой какой-то модуль компиляции также экспортирует символ с именем sin
. У компоновщика есть только имена символов для работы; если символ сталкивается, то, что происходит, сильно зависит от компоновщика, но в чем можно быть уверенным, так это в том, что он вряд ли будет тем, что требуется.
C ++ позволяет избежать коллизий пространства имен путем искажения символов. Искаженное имя содержит информацию о типе и пространстве имен. Однако если в программе на C ++ два символа с одним и тем же именем и типом, без или внутри одного и того же пространства имен, определены в нескольких единицах компиляции, существует одна и та же проблема.
Fortran даже старше, чем C. Как и в C, нет пространств имен, и опять же, для ссылок используются только имена символов. Так что проблема та же, по крайней мере для Fortran77.
Objective-C - это технически C с системой объектов, основанной на сообщениях; одни и те же правила связи. Objective-C ++ относится к Objective-C так же, как C ++ - к C.
В Java каждый символ существует в своем классе и, таким образом, окружен пространством имен. Поскольку классы связаны с модулями компиляции, это также предотвращает конфликты пространства имен, естественно. Но это также делает практически невозможным переопределение вещей в Java простым способом (хотя можно делать сумасшедшие вещи на уровне байт-кода).
В Python внешние функции доступны с помощью модулей. Если вы пишете import spamneggs
, то на самом деле происходит то, что вызывается специальная функция __import__
, а результат сохраняется как переменная с именем модуля. Тип этой переменной module
, но технически это просто ссылка на прославленный словарь, похожий на классы. Если вы напишите
import math
origsin = math.sin()
def mysin(v):
return origsin(v)
math.sin = mysin
в результате вы берете копию оригинала math.sin
и перезаписываете math.sin
переопределением. Поскольку экземпляры модуля являются общими для интерпретатора, если только не используется песочница, это переопределяет sin
прозрачным, неразрывным способом для всей программы. Это позволяет выбрать: либо переопределить всю программу, либо использовать «песочницу» для сохранения локальности.
Я не такой опытный кодировщик Ruby, поэтому не могу рассказать вам об этом.
В Лиспе и Схеме вы можете делать намного круче, чем просто переопределение, но все, что вы делаете, имеет только локальные эффекты без побочных эффектов. В Хаскеле это похоже.