Почему механизмы наследования C ++ непрозрачны? - PullRequest
1 голос
/ 12 сентября 2009

Почему, например, нет языковой поддержки для проверки vtable? Почему я не могу заменить функцию-член новой? У меня есть ощущение, что есть способы эффективно использовать такие функции.

Существуют ли другие языки, которые позволяют мне делать такие вещи?

Ответы [ 11 ]

17 голосов
/ 12 сентября 2009

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

8 голосов
/ 12 сентября 2009

Потому что это не должно быть реализовано как VTable, хотя обычно это так Короче говоря, в C ++ нет такой вещи как VTable!

8 голосов
/ 12 сентября 2009

C ++ - это язык, на котором вы никогда не «платите» за то, что не используете. Такая поддержка во время выполнения противоречила бы этой философии.

Существует множество языков (на более динамичном конце спектра), которые поддерживают это.

5 голосов
/ 12 января 2010

Основная причина заключается в том, что сохранение vtable в качестве детали реализации позволяет любой конкретной реализации оптимизировать ее по своему усмотрению; это означает, что он может, например, обрезать или даже полностью исключить vtable, если он может доказать, что для данного метода (или всех методов) нет виртуальных вызовов. Или он может заменить отправку vtable проверкой типа if-else, если, например, он видит, что есть только несколько альтернатив (это может быть выгодно, потому что предсказание ветвлений будет работать в этом случае, но не с vtables, а также потому, что тогда могут быть встроены ветви if-else). Он может переупорядочивать методы в vtable так, что наиболее часто вызываемые из них появляются раньше, или так, что те, которые обычно вызываются один за другим, заполняют смежные слоты в vtable, чтобы использовать преимущества кэширования. И так далее. Конечно, все эти реализации также сделали бы макет vtable совершенно непредсказуемым и, следовательно, бесполезным, если бы он подвергался (в соответствии со спецификацией языка) реализации.

Кроме того, vtables не так просты, как кажется. Например, компиляторам часто приходится генерировать thunks для исправления указателя this для таких вещей, как виртуальное наследование или множественное наследование в сочетании с ковариантными типами возврата. Это опять-таки то, что не имеет «единственного лучшего способа» сделать это (именно поэтому разные компиляторы делают это по-разному), и его стандартизация фактически потребует выбора конкретного способа.

Тем не менее, "переключение vtable" является потенциально полезным методом, если он представлен как конструкция более высокого уровня (так что оптимизации все еще возможны). Для примера см. UnrealScript, который позволяет определить несколько состояний для класса (один по умолчанию, другой с именем) и переопределить некоторые методы в именованных состояниях. Производные классы могут переопределять больше методов в существующих состояниях или добавлять свои собственные состояния и переопределять их. Кроме того, состояния могут расширять другие состояния (поэтому, если метод не переопределяется для определенного состояния, он возвращается к «родительскому» состоянию и т. Д., Пока цепочка не достигнет состояния по умолчанию). Для актерского моделирования (какими по сути являются игры) все это имеет большой смысл, поэтому в UnrealScript оно есть. И очевидный эффективный механизм реализации всего этого - переключение виртуальных таблиц, причем каждое состояние имеет отдельный виртуальный каталог.

3 голосов
/ 13 сентября 2009

Vtables существуют только при определенных обстоятельствах в некоторых компиляторах (то есть они не указаны в стандарте, но являются деталями реализации). Даже когда они существуют, они возникают только тогда, когда у вас есть виртуальные функции и вам нужно косвенное обращение для реализации полиморфизма. Когда это не требуется, их можно оптимизировать, сохраняя издержки косвенного обращения при вызове.

К сожалению (или иным образом, в зависимости от вашего мнения по этому вопросу ;-), C ++ не был разработан для поддержки исправлений обезьян. В некоторых случаях (например, COM) vtable является частью реализации, и вы можете за кулисами копаться. Однако это никогда не будет поддерживаться или переноситься.

3 голосов
/ 12 сентября 2009

JavaScript, Python и Ruby могут сделать это. В этих языках определения классов и экземпляров изменяются во время выполнения. Абстрактно, каждый объект и тип - это словарь переменных и методов-членов, которые можно изучать и обновлять.

Это невозможно в C ++, потому что для этого потребуется возможность перезаписи сгенерированного двоичного кода, что может привести к значительным потерям производительности.

2 голосов
/ 12 сентября 2009

Я думаю, что вы можете делать такие вещи в динамических языках, таких как Python:

>>> class X():
...     def bar(self): print "bar"
...     
>>> x = X()
>>> x.bar()
bar
>>> def foo(x): print "foo"
... 
>>> X.bar = foo
>>> x.bar()
foo

Разница со статическим языком, таким как C ++, состоит в том, что интерпретатор ищет все имена во время выполнения и затем решает, что делать.

В C ++, вероятно, есть другие решения проблемы «заменить функцию-член на другую», простейшим из которых может быть использование указателей на функции:

#include <iostream>

class X;
typedef void (*foo_func)(const X&);

void foo(const X&) { std::cout << "foo\n"; }
void bar(const X&) { std::cout << "bar\n"; }

class X
{
    foo_func f;
public:
    X(): f(foo) {}
    void foobar() { f(*this); }
    void switch_function(foo_func new_foo) { f = new_foo; }
};

int main()
{
    X x;
    x.foobar();
    x.switch_function(bar);
    x.foobar();
}

(foo и bar не используют аргумент X &, в этом примере аналогично примеру с Python)

1 голос
/ 12 января 2010

Есть ли другие языки? там, которые позволяют мне сделать такое вещи?

Objective-C (и Objective-C ++) позволяет заменять уже скомпилированные методы во время выполнения. Это либо лучшая комбинация статических и динамических техник, либо худшая, в зависимости от того, кого вы спрашиваете.

1 голос
/ 11 января 2010
  • Все, что вы можете сделать в C ++, вы можете сделать в прямом C с небольшим количеством смазки для локтя.
  • Любая скромная и разумная программа на C должна компилироваться на C ++.

Возможно, вам нужно реализовать свои собственные таблицы vtable без использования встроенного средства C ++. Вам будет очень весело с функциями указателя на член (ptmf)!

У вас возникнут проблемы с поиском скомпилированного языка с vtable introspection, потому что он мало востребован и его нелегко реализовать. Однако для устных языков ситуация иная.

1 голос
/ 12 сентября 2009

Я работаю над статически скомпилированным языком, который выставляет vtable, и, поверьте мне, это довольно немного hair , чтобы разоблачить.

...