От "Виртуальные функции в C++" :
Каждый раз, когда программе объявили виртуальную функцию, v - таблица создается для класса. V-таблица состоит из адресов к виртуальным функциям для классов, которые содержат одну или несколько виртуальных функций. Объект класса, содержащего виртуальную функцию, содержит виртуальный указатель, который указывает на базовый адрес виртуальной таблицы в памяти. Каждый раз, когда существует вызов виртуальной функции, v-таблица используется для разрешения к функциональному адресу. Объект класса, который содержит одну или несколько виртуальных функций, содержит виртуальный указатель, названный vptr в самом начале объекта в памяти. Следовательно размер объекта в этом случае увеличивается размером указателя. Этот vptr содержит базовый адрес виртуальной таблицы в памяти. Обратите внимание, что виртуальные таблицы являются конкретным классом, т.е. существует только одна виртуальная таблица для класса независимо от количества виртуальных функций, которые это содержит. Эта виртуальная таблица в свою очередь содержит базовые адреса одной или нескольких виртуальных функций класса. В то время, когда виртуальная функция вызвана на объекте, vptr того объекта обеспечивает базовый адрес виртуальной таблицы для того класса в памяти. Эта таблица используется для разрешения вызова функции, поскольку это содержит адреса всех виртуальных функций того класса. Это - то, как динамическое связывание разрешено во время вызова виртуальной функции.
Универсально, я полагаю, что ответ является "нет". Вы могли сделать некоторое искажение памяти для нахождения vtable, но Вы все еще не будете знать то, на что функциональная подпись похожа для вызова его. Что-либо, чего Вы хотели бы достигнуть с этой способностью (который поддерживает язык) должно быть возможным без доступа к vtable непосредственно или изменению его во времени выполнения. Также отметьте, спецификация языка C++ не делает , определяют, что vtables требуются - однако, именно так большинство компиляторов реализует виртуальные функции.
я верю , ответ здесь, "он зависит от реализации", так как спецификация не требует vtables во-первых. Однако на практике я полагаю, что все современные компиляторы только создают vtable, если класс имеет по крайней мере 1 виртуальную функцию. Существует пространство, наверху связанное с vtable и время, наверху связанное с вызыванием виртуальной функции по сравнению с невиртуальной функцией.
ответ - он, является неуказанным спецификацией языка, таким образом, это зависит от реализации. Вызов чистой виртуальной функции приводит к неопределенному поведению, если это не определяется (который это обычно не) (ISO/IEC 14882:2003 10.4-2). На практике это действительно выделяет слот в vtable для функции, но не присваивает адрес ему. Это оставляет vtable неполное, которое требует, чтобы производные классы реализовали функцию и завершили vtable. Некоторые реализации действительно просто помещают Нулевого указателя в vtable запись; другие реализации помещают указатель на фиктивный метод, который делает что-то подобное утверждению.
Примечание, что абстрактный класс может определить реализацию для чистой виртуальной функции, но что функция может только быть вызвана с синтаксисом квалифицированного идентификатора (т.е., полностью определив класс в имени метода, подобном вызову метода базового класса от производного класса). Это сделано для обеспечения простой в использовании реализации по умолчанию, все еще требуя, чтобы производный класс обеспечил переопределение.
Это добирается до края моего знания, таким образом, кто-то, помогите мне здесь, если я неправ!
я полагаю , что только функции, которые являются виртуальными в классе, испытывают хит производительности времени, связанный с вызыванием виртуальной функции по сравнению с невиртуальной функцией. Пространство наверху для класса там так или иначе. Обратите внимание, что, если существует vtable, существует только 1 на класс , не один на [1 110] объект .
я не верю времени выполнения виртуальной функции, которая переопределяется уменьшения по сравнению с вызыванием основной виртуальной функции. Однако существует дополнительное пространство наверху для класса, связанного с определением другого vtable для производного класса по сравнению с базовым классом.
http://www.codersource.net/published/view/325/virtual_functions_in.aspx (через путь назад машина)
http://en.wikipedia.org/wiki/Virtual_table
http://www.codesourcery.com/public/cxx-abi/abi.html#vtable
Удалите неинтересную часть строки (завершающие нули и точки), а затем сравните списки чисел.
import re
def mycmp(version1, version2):
def normalize(v):
return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
return cmp(normalize(version1), normalize(version2))
Это тот же подход, что и Pär Wieslander, но немного более компактный:
Вот несколько тестов, благодаря « Как сравнить две строки в формате версий, разделенных точками в Bash? »:
assert mycmp("1", "1") == 0
assert mycmp("2.1", "2.2") < 0
assert mycmp("3.0.4.10", "3.0.4.2") > 0
assert mycmp("4.08", "4.08.01") < 0
assert mycmp("3.2.1.9.8144", "3.2") > 0
assert mycmp("3.2", "3.2.1.9.8144") < 0
assert mycmp("1.2", "2.1") < 0
assert mycmp("2.1", "1.2") > 0
assert mycmp("5.6.7", "5.6.7") == 0
assert mycmp("1.01.1", "1.1.1") == 0
assert mycmp("1.1.1", "1.01.1") == 0
assert mycmp("1", "1.0") == 0
assert mycmp("1.0", "1") == 0
assert mycmp("1.0", "1.0.1") < 0
assert mycmp("1.0.1", "1.0") > 0
assert mycmp("1.0.2.0", "1.0.2") == 0
Мое предпочтительное решение:
Добавление в строку дополнительных нулей и использование только четырех первых легко понять, не требует никакого регулярного выражения, а лямбда более или менее читаема. Я использую две строки для удобства чтения, для меня элегантность коротка и проста.
def mycmp(version1,version2):
tup = lambda x: [int(y) for y in (x+'.0.0.0.0').split('.')][:4]
return cmp(tup(version1),tup(version2))
Самое трудное для чтения решение, но тем не менее однострочное! и использование итераторов для быстрой работы.
next((c for c in imap(lambda x,y:cmp(int(x or 0),int(y or 0)),
v1.split('.'),v2.split('.')) if c), 0)
то есть для Python2.6 и 3. + кстати, Python 2.5 и старше должны улавливать StopIteration.
def compare_version(v1, v2):
return cmp(*tuple(zip(*map(lambda x, y: (x or 0, y or 0),
[int(x) for x in v1.split('.')], [int(y) for y in v2.split('.')]))))
Это однострочный (разделен для удобства чтения). Не уверен в удобочитаемости ...
Удалить завершающие .0
и .00
с помощью регулярного выражения, разделить
и использовать функцию cmp
, которая правильно сравнивает массивы:
def mycmp(v1,v2):
c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
return cmp(c1,c2)
И, конечно, вы можете преобразовать его в однострочное, если не возражаете против длинных строк.
Это немного более компактно, чем ваше предложение. Вместо того, чтобы заполнять более короткую версию нулями, я удаляю конечные нули из списков версий после разделения.
def normalize_version(v):
parts = [int(x) for x in v.split(".")]
while parts[-1] == 0:
parts.pop()
return parts
def mycmp(v1, v2):
return cmp(normalize_version(v1), normalize_version(v2))
Нет необходимости перебирать кортежи версий. Встроенный оператор сравнения списков и кортежей уже работает точно так, как вы этого хотите. Вам просто нужно расширить списки версий до соответствующей длины. В python 2.6 вы можете использовать izip_longest для заполнения последовательностей.
from itertools import izip_longest
def version_cmp(v1, v2):
parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
parts1, parts2 = zip(*izip_longest(parts1, parts2, fillvalue=0))
return cmp(parts1, parts2)
В более ранних версиях требуется взлом карты.
def version_cmp(v1, v2):
parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
parts1, parts2 = zip(*map(lambda p1,p2: (p1 or 0, p2 or 0), parts1, parts2))
return cmp(parts1, parts2)
Считается ли повторное использование элегантностью в данном случае? :)
# pkg_resources is in setuptools
# See http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities
def mycmp(a, b):
from pkg_resources import parse_version as V
return cmp(V(a),V(b))