Вот я смотрю очередной раз на трейты. Первоистчник говорит:
Traits do not specify any state variables, and the methods provided by traits never directly access state variables.
Далее смотрим первую попавшуюся статью по PHP и видим:
trait Id
{
protected $id;
public function getId()
{
return $this->id;
}
}
Т.е., в PHP трейты - это не трейты, я верно понял?
И теперь вот что я хотел узнать - как соотносятся по производительности трейты с одиночным наследованием реализации. Тут я слегка упираюсь в то, что я не понимаю, как реализованы вирт. ф-ии в С++ при множественном наследовании. Как я понял, если класс Child наследует от Parent1 и Parent2, и мы хотим вызвать виртуальный метод Parent1::Method, то мы должны где-то найти указатель на этот метод. Метод ищется в VMT и Child содержит 2 VMT, для каждого из Parent.
И получается, чтобы найти этот VMT, нам нужно сначала вызвать некую ф-ю VMTOffsetOfParent1(Child). Эта функция может вызываться для всех потомков Parent1, и может принимать на вход только какое-то число, идентификатор класса, а возвращать она должна указатель. Такое отображение можно реализовать только с помощью хеш-таблицы, b-дерева или иного объекта с логарифмическим временем доступа по отношению к количеству потомков Parent1.
По сравнению с этим, при одиночном наследовании вызов вирт.метода осуществляется за линейное время.
Если Parent1 и Parent2 становятся трейтами, то опять же нам нужно вызвать функцию GetParent(Child), которая опять же требует хеш-таблицы для своей реализации, со временем доступа, логарифмическим по отношению к количеству классов, воплощающих этот трейт.
Вывод отсюда такой: одиночное наследование реализации с таблицей виртуальных методов даёт существенный выигрыш в производительности по сравнению с множественным наследованием и трейтами. Мы можем это по всячески оптимизировать, кешируя найденные трейты или VMT, но это не отменяет исходной разницы.
Наверное, я зря сюда приплёл вирт. ф-ии, поскольку затратная операция здесь - скорее dynamic_cast. Вот нашёл статейку, как подобрать хеш-функцию, чтобы не было конфликтов между классами.
Верно я мыслю или нет? В свете этого, верно ли, с точки зрения производительности, решение отказаться от наследования реализации, принятое в Rust и Golang?
А зачем мне это нужно: нужно быстро придумать, как реализовать ООП в Яре. Понятно, что нужно включить минимум для начала (потому что версия только 0.4). Но любой минимум потом оказывается более, чем минимальным, и включив что-то в язык, потом уже не избавишься. Поэтому тэг «Яр» здесь должен стоять, уж извините. Он особенно хорош тем, что те, кому я надоел, ставят этот тег в список игнорируемых.