2016-12-22 5 views
1

Я в настоящее время разрабатывает язык программирования и мне любопытно, как решить эту проблему:Как вызывается функция в подтипе?

Предположим, что у меня есть класс (или интерфейс) А, который выглядит следующим образом:

class A { // size is 4 bytes 
    int32 a = 0; 
} 

и второй класс B, который расширяет его и выглядит следующим образом:

class B extends A { // size is 8 bytes 
    int32 b = 0; 
} 

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

int32 f(A first, A second) { 
    return first.a + second.a; 
} 

Если я называю это с двумя Bs, однако, second.a не будет находиться в том же месте, что и если бы он был вызван с двумя As, потому что первый параметр сдвинул бы его. Мои текущие мысли для решения этого:

  1. Запрет параметров неизвестного размера, и заставляя его быть переданы в качестве указателя или ссылки (я думаю, что это то, что делает Rust)
  2. Дать все следующие информации стек вызовов: указатель на второй, указатель на второй, невариантный размер, первый, второй
  3. Создание функции для каждого возможного размера первого и второго и определение того, какой вызов вызывать во время компиляции, если известно, или во время выполнения с использованием vtables.

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

Третья идея потребует создания множества функций (функция, которая принимает 5 параметров, которые могут быть из 20 разных подтипов, потребует создания 100 одинаковых фрагментов кода, если она называется только одной с неизвестным типом params), и для этого потребуется vtable для каждого класса, который использует только одну функцию. Кроме того, функция в уже скомпилированной библиотеке не может использоваться с новым подтипом.

Сочетание 2 и 3 и создание двух версий тех же функций, которые допускают только тип, а другой, который принимает подтипы, также могут решить некоторые из этих проблем.

Мне интересно, есть ли лучшие решения для этого и как реализуют другие языки, такие как C++.

+3

В C++ вы будете * нарезать * объект, то есть объект, который скопирован в стек как параметр, по существу, является первым размером 'sizeof (A)' байта объекта, предоставленного в качестве аргумента. Обычно это рассматривается как опасность перехода по значению. –

+1

Я не уверен, что это правильный сайт SE для этого вопроса, но я не могу думать, куда он должен идти. Возможно CS.SE. –

+0

@KyleStrand: что, если функция вызывает виртуальную функцию на полученном параметре, а функция переопределения использует значение b класса, которое было нарезано? – Runemoro

ответ

3

В C++, вызывая f(A) с параметром подтипа B по значению эквивалентен

f(static_cast<const A&>(b)); 

static_cast может привести к памяти по тому же адресу просто переосмыслили как начало более короткий блок данных, или прозрачно сначала добавить некоторый смещение (если A не является первым базовым классом или является виртуальным). После этого внутренне создается экземпляр-копия A. В любом случае информация, которая была добавлена ​​B, полностью потеряна вместе с переопределением виртуальных функций. Для всех целей то, что передано, уже не является B.

Для динамического полиморфизма нужна ссылка или указатель, в основном по причинам, которые вы начертите. Но если вы хотите передать «ссылку по значению», самым простым решением, вероятно, будет передача ссылки на копию объекта.Обратите внимание, что в этом случае каждый объект должен «знать», какой тип он должен назвать правильным конструктором копирования, или быть полученным из общего суперкласса и реализовать некоторую форму clone().

Смежные вопросы